Files
c3c/src/compiler/llvm_codegen_expr.c
Christoffer Lerno 990918b609 LLVM Codegen
2019-11-20 17:09:25 +01:00

379 lines
14 KiB
C

// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "llvm_codegen_internal.h"
#include "compiler_internal.h"
LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
{
switch (expr->expr_kind)
{
case EXPR_IDENTIFIER:
return expr->identifier_expr.decl->var.backend_ref;
case EXPR_CONST:
case EXPR_TYPE:
UNREACHABLE
case EXPR_UNARY:
{
UnaryOp op = unaryop_from_token(expr->unary_expr.operator);
assert(op == UNARYOP_DEREF);
return gencontext_emit_expr(context, expr->unary_expr.expr);
}
default:
TODO;
}
return NULL;
}
static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr *expr)
{
LLVMValueRef rhs = gencontext_emit_expr(context, expr->expr_cast.expr);
switch (expr->expr_cast.kind)
{
case CAST_ERROR:
UNREACHABLE
case CAST_PTRPTR:
return LLVMBuildPointerCast(context->builder, rhs, LLVMTYPE(expr->type), "ptrptr");
case CAST_PTRXI:
return LLVMBuildPtrToInt(context->builder, rhs, LLVMTYPE(expr->type), "ptrxi");
case CAST_VARRPTR:
TODO
case CAST_ARRPTR:
TODO
case CAST_STRPTR:
TODO
case CAST_PTRBOOL:
return LLVMBuildICmp(context->builder, LLVMIntNE, rhs, LLVMConstPointerNull(LLVMTYPE(expr->type->canonical->base)), "ptrbool");
case CAST_BOOLINT:
return LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "boolsi");
case CAST_FPBOOL:
return LLVMBuildFCmp(context->builder, LLVMRealUNE, rhs, LLVMConstNull(LLVMTypeOf(rhs)), "fpbool");
case CAST_BOOLFP:
return LLVMBuildSIToFP(context->builder, rhs, LLVMTYPE(expr->type), "boolfp");
case CAST_INTBOOL:
return LLVMBuildICmp(context->builder, LLVMIntNE, rhs, LLVMConstNull(LLVMTypeOf(rhs)), "intbool");
case CAST_FPFP:
return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type)
? LLVMBuildFPTrunc(context->builder, rhs, LLVMTYPE(expr->type), "fpfptrunc")
: LLVMBuildFPExt(context->builder, rhs, LLVMTYPE(expr->type), "fpfpext");
case CAST_FPSI:
return LLVMBuildFPToSI(context->builder, rhs, LLVMTYPE(expr->type), "fpsi");
case CAST_FPUI:
return LLVMBuildFPToUI(context->builder, rhs, LLVMTYPE(expr->type), "fpui");
case CAST_SISI:
return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type)
? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "sisitrunc")
: LLVMBuildSExt(context->builder, rhs, LLVMTYPE(expr->type), "sisiext");
case CAST_SIUI:
return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type)
? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "siuitrunc")
: LLVMBuildZExt(context->builder, rhs, LLVMTYPE(expr->type), "siuiext");
break;
case CAST_SIFP:
return LLVMBuildSIToFP(context->builder, rhs, LLVMTYPE(expr->type), "sifp");
case CAST_XIPTR:
return LLVMBuildIntToPtr(context->builder, rhs, LLVMTYPE(expr->type), "xiptr");
case CAST_UISI:
return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type)
? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "uisitrunc")
: LLVMBuildZExt(context->builder, rhs, LLVMTYPE(expr->type), "uisiext");
case CAST_UIUI:
return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type)
? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "uiuitrunc")
: LLVMBuildZExt(context->builder, rhs, LLVMTYPE(expr->type), "uiuiext");
case CAST_UIFP:
return LLVMBuildUIToFP(context->builder, rhs, LLVMTYPE(expr->type), "uifp");
case CAST_ENUMSI:
TODO
}
}
LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr)
{
UnaryOp unary_op = unaryop_from_token(expr->unary_expr.operator);
switch (unary_op)
{
case UNARYOP_ERROR:
FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator);
case UNARYOP_NOT:
return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), LLVMConstInt(type_bool->backend_type, 1, 0), "not");
case UNARYOP_BITNEG:
return LLVMBuildNot(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "bnot");
case UNARYOP_NEG:
if (type_is_float(expr->unary_expr.expr->type->canonical))
{
return LLVMBuildFNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "fneg");
}
return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg");
case UNARYOP_ADDR:
return gencontext_emit_address(context, expr->unary_expr.expr);
case UNARYOP_DEREF:
return LLVMBuildLoad(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "deref");
case UNARYOP_INC:
TODO
case UNARYOP_DEC:
TODO
}
}
static LLVMValueRef gencontext_emit_assign(GenContext *context, Expr *left, LLVMValueRef right)
{
LLVMValueRef addr = gencontext_emit_address(context, left);
return LLVMBuildStore(context->builder, right, addr);
}
static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *expr, BinaryOp op)
{
// Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E)
// For vector implementation.
// Set up basic blocks, following Cone
LLVMBasicBlockRef start_block = LLVMGetInsertBlock(context->builder);
LLVMBasicBlockRef phi_block = LLVMCreateBasicBlockInContext(context->context, op == BINARYOP_AND ? "and.phi" : "or.phi");
LLVMBasicBlockRef rhs_block = LLVMCreateBasicBlockInContext(context->context, op == BINARYOP_AND ? "and.rhs" : "or.rhs");
// Generate left-hand condition and conditional branch
LLVMValueRef lhs = gencontext_emit_expr(context, expr->binary_expr.left);
if (op == BINARYOP_AND)
{
gencontext_emit_cond_br(context, lhs, rhs_block, phi_block);
}
else
{
gencontext_emit_cond_br(context, lhs, phi_block, rhs_block);
}
gencontext_emit_block(context, rhs_block);
LLVMValueRef rhs = gencontext_emit_expr(context, expr->binary_expr.right);
gencontext_emit_br(context, phi_block);
// Generate phi
gencontext_emit_block(context, phi_block);
LLVMValueRef phi = LLVMBuildPhi(context->builder, type_bool->backend_type, "val");
// Simplify for LLVM by entering the constants we already know of.
LLVMValueRef result_on_skip = LLVMConstInt(LLVMInt1TypeInContext(context->context), op == BINARYOP_AND ? 0 : 1, false);
LLVMValueRef logicValues[2] = { result_on_skip, rhs };
LLVMBasicBlockRef blocks[2] = { start_block, rhs_block };
LLVMAddIncoming(phi, logicValues, blocks, 2);
return phi;
}
static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, bool load_lhs_after_rhs, BinaryOp binary_op)
{
if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR)
{
return gencontext_emit_logical_and_or(context, expr, binary_op);
}
Type *type = expr->type->canonical;
Expr *lhs = expr->binary_expr.left;
Expr *rhs = expr->binary_expr.right;
LLVMValueRef lhs_value;
LLVMValueRef rhs_value;
if (load_lhs_after_rhs)
{
rhs_value = gencontext_emit_expr(context, rhs);
lhs_value = gencontext_emit_expr(context, lhs);
}
else
{
lhs_value = gencontext_emit_expr(context, lhs);
rhs_value = gencontext_emit_expr(context, rhs);
}
bool is_float = type_is_float(type);
switch (binary_op)
{
case BINARYOP_ERROR:
UNREACHABLE
case BINARYOP_MULT:
return is_float ? LLVMBuildFMul(context->builder, lhs_value, rhs_value, "fmul") : LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul");
case BINARYOP_SUB:
if (lhs->type->canonical->type_kind == TYPE_POINTER)
{
if (lhs->type->canonical == rhs->type->canonical) return LLVMBuildPtrDiff(context->builder, lhs_value, rhs_value, "ptrdiff");
rhs_value = LLVMBuildNeg(context->builder, rhs_value, "");
return LLVMBuildGEP2(context->builder, gencontext_get_llvm_type(context, lhs->type->canonical), lhs_value, &rhs_value, 1, "ptrsub");
}
if (is_float) return LLVMBuildFSub(context->builder, lhs_value, rhs_value, "fsub");
// Consider UB version instead.
return LLVMBuildSub(context->builder, lhs_value, rhs_value, "sub");
case BINARYOP_ADD:
if (lhs->type->canonical->type_kind == TYPE_POINTER)
{
assert(type_is_integer(lhs->type->canonical));
return LLVMBuildGEP2(context->builder, gencontext_get_llvm_type(context, lhs->type->canonical), lhs_value, &rhs_value, 1, "ptradd");
}
if (is_float) return LLVMBuildFAdd(context->builder, lhs_value, rhs_value, "fadd");
// Consider UB version instead.
return LLVMBuildAdd(context->builder, lhs_value, rhs_value, "add");
case BINARYOP_DIV:
if (is_float) return LLVMBuildFDiv(context->builder, lhs_value, rhs_value, "fdiv");
return type_is_unsigned(type)
? LLVMBuildUDiv(context->builder, lhs_value, rhs_value, "udiv")
: LLVMBuildSDiv(context->builder, lhs_value, rhs_value, "sdiv");
case BINARYOP_MOD:
return type_is_unsigned(type)
? LLVMBuildURem(context->builder, lhs_value, rhs_value, "umod")
: LLVMBuildSRem(context->builder, lhs_value, rhs_value, "smod");
case BINARYOP_SHR:
return type_is_unsigned(type)
? LLVMBuildLShr(context->builder, lhs_value, rhs_value, "lshr")
: LLVMBuildAShr(context->builder, lhs_value, rhs_value, "ashr");
case BINARYOP_SHL:
return LLVMBuildShl(context->builder, lhs_value, rhs_value, "shr");
case BINARYOP_BIT_AND:
return LLVMBuildAnd(context->builder, lhs_value, rhs_value, "and");
case BINARYOP_BIT_OR:
return LLVMBuildOr(context->builder, lhs_value, rhs_value, "or");
case BINARYOP_BIT_XOR:
return LLVMBuildXor(context->builder, lhs_value, rhs_value, "xor");
case BINARYOP_EQ:
// Unordered?
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq");
return LLVMBuildICmp(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq");
case BINARYOP_NE:
// Unordered?
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUNE, lhs_value, rhs_value, "neq");
return LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq");
case BINARYOP_GE:
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUGE, lhs_value, rhs_value, "ge");
return type_is_unsigned(type)
? LLVMBuildICmp(context->builder, LLVMIntUGE, lhs_value, rhs_value, "ge")
: LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, rhs_value, "ge");
case BINARYOP_GT:
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUGT, lhs_value, rhs_value, "gt");
return type_is_unsigned(type)
? LLVMBuildICmp(context->builder, LLVMIntUGT, lhs_value, rhs_value, "gt")
: LLVMBuildICmp(context->builder, LLVMIntSGT, lhs_value, rhs_value, "gt");
case BINARYOP_LE:
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "le");
return type_is_unsigned(type)
? LLVMBuildICmp(context->builder, LLVMIntULE, lhs_value, rhs_value, "le")
: LLVMBuildICmp(context->builder, LLVMIntSLE, lhs_value, rhs_value, "le");
case BINARYOP_LT:
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt");
return type_is_unsigned(type)
? LLVMBuildICmp(context->builder, LLVMIntULT, lhs_value, rhs_value, "lt")
: LLVMBuildICmp(context->builder, LLVMIntSLT, lhs_value, rhs_value, "lt");
case BINARYOP_AND:
case BINARYOP_OR:
UNREACHABLE
case BINARYOP_ASSIGN:
case BINARYOP_MULT_ASSIGN:
case BINARYOP_ADD_ASSIGN:
case BINARYOP_SUB_ASSIGN:
case BINARYOP_DIV_ASSIGN:
case BINARYOP_MOD_ASSIGN:
case BINARYOP_AND_ASSIGN:
case BINARYOP_OR_ASSIGN:
case BINARYOP_BIT_AND_ASSIGN:
case BINARYOP_BIT_OR_ASSIGN:
case BINARYOP_BIT_XOR_ASSIGN:
case BINARYOP_SHR_ASSIGN:
case BINARYOP_SHL_ASSIGN:
UNREACHABLE
}
}
static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr)
{
BinaryOp binary_op = binaryop_from_token(expr->binary_expr.operator);
if (binary_op > BINARYOP_ASSIGN)
{
BinaryOp base_op = binaryop_assign_base_op(binary_op);
assert(base_op != BINARYOP_ERROR);
LLVMValueRef value = gencontext_emit_binary(context, expr, true, base_op);
gencontext_emit_assign(context, expr->binary_expr.left, value);
return value;
}
if (binary_op == BINARYOP_ASSIGN)
{
LLVMValueRef value = gencontext_emit_expr(context, expr->binary_expr.right);
gencontext_emit_assign(context, expr->binary_expr.left, value);
return value;
}
return gencontext_emit_binary(context, expr, false, binary_op);
}
static LLVMValueRef gencontext_emit_identifier_expr(GenContext *context, Expr *expr)
{
return LLVMBuildLoad2(context->builder, expr->identifier_expr.decl->var.type->canonical->backend_type,
expr->identifier_expr.decl->var.backend_ref, expr->identifier_expr.decl->name.string);
}
LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr)
{
LLVMTypeRef type = gencontext_get_llvm_type(context, expr->type->canonical);
switch (expr->const_expr.type)
{
case CONST_INT:
return LLVMConstInt(type, expr->const_expr.i, type_is_unsigned(expr->type->canonical) ? false : true);
case CONST_FLOAT:
return LLVMConstReal(type, (double) expr->const_expr.f);
case CONST_NIL:
return LLVMConstNull(type);
case CONST_BOOL:
return LLVMConstInt(type, expr->const_expr.b ? 1 : 0, false);
case CONST_STRING:
return LLVMConstStringInContext(context->context,
expr->const_expr.string.chars,
expr->const_expr.string.len,
false);
}
UNREACHABLE
}
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
{
switch (expr->expr_kind)
{
case EXPR_POISONED:
UNREACHABLE
case EXPR_UNARY:
return gencontext_emit_unary_expr(context, expr);
case EXPR_TRY:
break;
case EXPR_CONST:
return gencontext_emit_const_expr(context, expr);
case EXPR_BINARY:
return gencontext_emit_binary_expr(context, expr);
case EXPR_CONDITIONAL:
break;
case EXPR_POST_UNARY:
break;
case EXPR_TYPE:
break;
case EXPR_IDENTIFIER:
return gencontext_emit_identifier_expr(context, expr);
case EXPR_TYPE_ACCESS:
break;
case EXPR_CALL:
break;
case EXPR_SIZEOF:
break;
case EXPR_SUBSCRIPT:
break;
case EXPR_ACCESS:
break;
case EXPR_STRUCT_VALUE:
break;
case EXPR_STRUCT_INIT_VALUES:
break;
case EXPR_INITIALIZER_LIST:
break;
case EXPR_EXPRESSION_LIST:
break;
case EXPR_CAST:
return gencontext_emit_cast_expr(context, expr);
}
TODO
}