562 lines
19 KiB
C
562 lines
19 KiB
C
//
|
|
// Created by servostar on 5/28/24.
|
|
//
|
|
|
|
#include <llvm/llvm-ir/expr.h>
|
|
#include <llvm/llvm-ir/types.h>
|
|
#include <mem/cache.h>
|
|
#include <sys/log.h>
|
|
|
|
BackendError impl_bitwise_operation(LLVMBackendCompileUnit* unit,
|
|
LLVMLocalScope* scope,
|
|
LLVMBuilderRef builder,
|
|
Operation* operation,
|
|
LLVMValueRef* llvm_result) {
|
|
Expression* rhs = NULL;
|
|
Expression* lhs = NULL;
|
|
LLVMValueRef llvm_rhs = NULL;
|
|
LLVMValueRef llvm_lhs = NULL;
|
|
|
|
if (operation->impl.bitwise == BitwiseNot) {
|
|
// single operand
|
|
rhs = g_array_index(operation->operands, Expression*, 0);
|
|
impl_expr(unit, scope, builder, rhs, FALSE, 0, &llvm_rhs);
|
|
} else {
|
|
// two operands
|
|
lhs = g_array_index(operation->operands, Expression*, 0);
|
|
impl_expr(unit, scope, builder, lhs, FALSE, 0, &llvm_lhs);
|
|
|
|
rhs = g_array_index(operation->operands, Expression*, 1);
|
|
impl_expr(unit, scope, builder, rhs, FALSE, 0, &llvm_rhs);
|
|
}
|
|
|
|
switch (operation->impl.bitwise) {
|
|
case BitwiseAnd:
|
|
*llvm_result =
|
|
LLVMBuildAnd(builder, llvm_lhs, llvm_rhs, "bitwise and");
|
|
break;
|
|
case BitwiseOr:
|
|
*llvm_result =
|
|
LLVMBuildOr(builder, llvm_lhs, llvm_rhs, "bitwise or");
|
|
break;
|
|
case BitwiseXor:
|
|
*llvm_result =
|
|
LLVMBuildXor(builder, llvm_lhs, llvm_rhs, "bitwise xor");
|
|
break;
|
|
case BitwiseNot:
|
|
*llvm_result = LLVMBuildNot(builder, llvm_rhs, "bitwise not");
|
|
break;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert any integral type (integer) to a boolean value.
|
|
* A boolean value hereby meaning an integer of the same type as the
|
|
* input value but with the value of either 0 or one.
|
|
* @param builder
|
|
* @param integral
|
|
* @return
|
|
*/
|
|
[[maybe_unused]]
|
|
static LLVMValueRef convert_integral_to_boolean(LLVMBuilderRef builder,
|
|
LLVMValueRef integral) {
|
|
// type of input
|
|
LLVMTypeRef valueType = LLVMTypeOf(integral);
|
|
// zero value of same type as integral
|
|
LLVMValueRef zero = LLVMConstIntOfString(valueType, "0", 10);
|
|
// returns 1 if integral is not zero and zero otherwise
|
|
return LLVMBuildICmp(builder, LLVMIntNE, zero, integral, "to boolean");
|
|
}
|
|
|
|
BackendError impl_logical_operation(LLVMBackendCompileUnit* unit,
|
|
LLVMLocalScope* scope,
|
|
LLVMBuilderRef builder,
|
|
Operation* operation,
|
|
LLVMValueRef* llvm_result) {
|
|
Expression* rhs = NULL;
|
|
Expression* lhs = NULL;
|
|
LLVMValueRef llvm_rhs = NULL;
|
|
LLVMValueRef llvm_lhs = NULL;
|
|
|
|
if (operation->impl.logical == LogicalNot) {
|
|
// single operand
|
|
rhs = g_array_index(operation->operands, Expression*, 0);
|
|
impl_expr(unit, scope, builder, rhs, FALSE, 0, &llvm_rhs);
|
|
} else {
|
|
// two operands
|
|
lhs = g_array_index(operation->operands, Expression*, 0);
|
|
impl_expr(unit, scope, builder, lhs, FALSE, 0, &llvm_lhs);
|
|
|
|
rhs = g_array_index(operation->operands, Expression*, 1);
|
|
impl_expr(unit, scope, builder, rhs, FALSE, 0, &llvm_rhs);
|
|
}
|
|
|
|
switch (operation->impl.logical) {
|
|
case LogicalAnd:
|
|
*llvm_result =
|
|
LLVMBuildAnd(builder, llvm_lhs, llvm_rhs, "logical and");
|
|
break;
|
|
case LogicalOr:
|
|
*llvm_result =
|
|
LLVMBuildOr(builder, llvm_lhs, llvm_rhs, "logical or");
|
|
break;
|
|
case LogicalXor:
|
|
*llvm_result =
|
|
LLVMBuildXor(builder, llvm_lhs, llvm_rhs, "logical xor");
|
|
break;
|
|
case LogicalNot:
|
|
*llvm_result = LLVMBuildNot(builder, llvm_rhs, "logical not");
|
|
break;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
static LLVMBool is_floating_point(Type* value) {
|
|
if (value->kind == TypeKindPrimitive) {
|
|
return value->impl.primitive == Float;
|
|
}
|
|
|
|
if (value->kind == TypeKindComposite) {
|
|
return value->impl.composite.primitive == Float;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static LLVMBool is_integral(Type* value) {
|
|
if (value->kind == TypeKindPrimitive) {
|
|
return value->impl.primitive == Int;
|
|
}
|
|
|
|
if (value->kind == TypeKindComposite) {
|
|
return value->impl.composite.primitive == Int;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BackendError impl_relational_operation(LLVMBackendCompileUnit* unit,
|
|
LLVMLocalScope* scope,
|
|
LLVMBuilderRef builder,
|
|
Operation* operation,
|
|
LLVMValueRef* llvm_result) {
|
|
Expression* rhs = NULL;
|
|
Expression* lhs = NULL;
|
|
LLVMValueRef llvm_rhs = NULL;
|
|
LLVMValueRef llvm_lhs = NULL;
|
|
|
|
// two operands
|
|
lhs = g_array_index(operation->operands, Expression*, 0);
|
|
impl_expr(unit, scope, builder, lhs, FALSE, 0, &llvm_lhs);
|
|
|
|
rhs = g_array_index(operation->operands, Expression*, 1);
|
|
impl_expr(unit, scope, builder, rhs, FALSE, 0, &llvm_rhs);
|
|
|
|
if (is_integral(rhs->result)) {
|
|
// integral type
|
|
LLVMIntPredicate operator= 0;
|
|
|
|
switch (operation->impl.relational) {
|
|
case Equal:
|
|
operator= LLVMIntEQ;
|
|
break;
|
|
case Greater:
|
|
operator= LLVMIntSGT;
|
|
break;
|
|
case Less:
|
|
operator= LLVMIntSLT;
|
|
break;
|
|
}
|
|
|
|
*llvm_result = LLVMBuildICmp(builder, operator, llvm_lhs, llvm_rhs,
|
|
"integral comparison");
|
|
|
|
} else if (is_floating_point(rhs->result)) {
|
|
// integral type
|
|
LLVMRealPredicate operator= 0;
|
|
|
|
switch (operation->impl.relational) {
|
|
case Equal:
|
|
operator= LLVMRealOEQ;
|
|
break;
|
|
case Greater:
|
|
operator= LLVMRealOGT;
|
|
break;
|
|
case Less:
|
|
operator= LLVMRealOLT;
|
|
break;
|
|
}
|
|
|
|
*llvm_result = LLVMBuildFCmp(builder, operator, llvm_lhs, llvm_rhs,
|
|
"floating point comparison");
|
|
} else {
|
|
PANIC("invalid type for relational operator");
|
|
}
|
|
|
|
// *llvm_result = convert_integral_to_boolean(builder, *llvm_result);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
BackendError impl_arithmetic_operation(LLVMBackendCompileUnit* unit,
|
|
LLVMLocalScope* scope,
|
|
LLVMBuilderRef builder,
|
|
Operation* operation,
|
|
LLVMValueRef* llvm_result) {
|
|
Expression* rhs = NULL;
|
|
Expression* lhs = NULL;
|
|
LLVMValueRef llvm_rhs = NULL;
|
|
LLVMValueRef llvm_lhs = NULL;
|
|
|
|
if (operation->impl.arithmetic == Negate) {
|
|
// single operand
|
|
rhs = g_array_index(operation->operands, Expression*, 0);
|
|
impl_expr(unit, scope, builder, rhs, FALSE, 0, &llvm_rhs);
|
|
} else {
|
|
// two operands
|
|
lhs = g_array_index(operation->operands, Expression*, 0);
|
|
impl_expr(unit, scope, builder, lhs, FALSE, 0, &llvm_lhs);
|
|
|
|
rhs = g_array_index(operation->operands, Expression*, 1);
|
|
impl_expr(unit, scope, builder, rhs, FALSE, 0, &llvm_rhs);
|
|
}
|
|
|
|
if (is_integral(rhs->result)) {
|
|
|
|
switch (operation->impl.arithmetic) {
|
|
case Add:
|
|
*llvm_result = LLVMBuildNSWAdd(builder, llvm_lhs, llvm_rhs,
|
|
"signed integer addition");
|
|
break;
|
|
case Sub:
|
|
*llvm_result = LLVMBuildNSWSub(builder, llvm_lhs, llvm_rhs,
|
|
"signed integer subtraction");
|
|
break;
|
|
case Mul:
|
|
*llvm_result = LLVMBuildNSWMul(builder, llvm_lhs, llvm_rhs,
|
|
"signed integer multiply");
|
|
break;
|
|
case Div:
|
|
*llvm_result = LLVMBuildSDiv(builder, llvm_lhs, llvm_rhs,
|
|
"signed integer divide");
|
|
break;
|
|
case Negate:
|
|
*llvm_result =
|
|
LLVMBuildNeg(builder, llvm_rhs, "signed integer negate");
|
|
break;
|
|
}
|
|
|
|
} else if (is_floating_point(rhs->result)) {
|
|
|
|
switch (operation->impl.arithmetic) {
|
|
case Add:
|
|
*llvm_result = LLVMBuildFAdd(builder, llvm_lhs, llvm_rhs,
|
|
"floating point addition");
|
|
break;
|
|
case Sub:
|
|
*llvm_result = LLVMBuildFSub(builder, llvm_lhs, llvm_rhs,
|
|
"floating point subtraction");
|
|
break;
|
|
case Mul:
|
|
*llvm_result = LLVMBuildFMul(builder, llvm_lhs, llvm_rhs,
|
|
"floating point multiply");
|
|
break;
|
|
case Div:
|
|
*llvm_result = LLVMBuildFDiv(builder, llvm_lhs, llvm_rhs,
|
|
"floating point divide");
|
|
break;
|
|
case Negate:
|
|
*llvm_result =
|
|
LLVMBuildFNeg(builder, llvm_rhs, "floating point negate");
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
PANIC("invalid type for arithmetic operator");
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
BackendError impl_operation(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
|
LLVMBuilderRef builder, Operation* operation,
|
|
LLVMValueRef* llvm_result) {
|
|
BackendError err;
|
|
|
|
switch (operation->kind) {
|
|
case Bitwise:
|
|
err = impl_bitwise_operation(unit, scope, builder, operation,
|
|
llvm_result);
|
|
break;
|
|
case Boolean:
|
|
err = impl_logical_operation(unit, scope, builder, operation,
|
|
llvm_result);
|
|
break;
|
|
case Relational:
|
|
err = impl_relational_operation(unit, scope, builder, operation,
|
|
llvm_result);
|
|
break;
|
|
case Arithmetic:
|
|
err = impl_arithmetic_operation(unit, scope, builder, operation,
|
|
llvm_result);
|
|
break;
|
|
default:
|
|
PANIC("Invalid operator");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
BackendError impl_transmute(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
|
LLVMBuilderRef builder, Transmute* transmute,
|
|
LLVMValueRef* llvm_result) {
|
|
|
|
LLVMValueRef operand = NULL;
|
|
impl_expr(unit, scope, builder, transmute->operand, FALSE, 0, &operand);
|
|
|
|
LLVMTypeRef target_type = NULL;
|
|
BackendError err = get_type_impl(unit, scope->func_scope->global_scope,
|
|
transmute->targetType, &target_type);
|
|
// if target type is valid
|
|
if (err.kind == Success) {
|
|
*llvm_result =
|
|
LLVMBuildBitCast(builder, operand, target_type, "transmute");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static LLVMBool is_type_signed(const Type* type) {
|
|
switch (type->kind) {
|
|
case TypeKindPrimitive:
|
|
return 1;
|
|
case TypeKindComposite:
|
|
return type->impl.composite.sign == Signed;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
BackendError impl_typecast(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
|
LLVMBuilderRef builder, TypeCast* typecast,
|
|
bool reference, LLVMValueRef* llvm_result) {
|
|
LLVMValueRef operand = NULL;
|
|
impl_expr(unit, scope, builder, typecast->operand, reference, 0, &operand);
|
|
|
|
LLVMTypeRef target_type = NULL;
|
|
BackendError err = get_type_impl(unit, scope->func_scope->global_scope,
|
|
typecast->targetType, &target_type);
|
|
// if target type is valid
|
|
if (err.kind != Success) {
|
|
return err;
|
|
}
|
|
|
|
LLVMBool dst_signed = is_type_signed(typecast->targetType);
|
|
LLVMBool src_signed = is_type_signed(typecast->operand->result);
|
|
const LLVMOpcode opcode =
|
|
LLVMGetCastOpcode(operand, src_signed, target_type, dst_signed);
|
|
*llvm_result =
|
|
LLVMBuildCast(builder, opcode, operand, target_type, "expr.typecast");
|
|
|
|
return err;
|
|
}
|
|
|
|
BackendError impl_variable_load(LLVMBackendCompileUnit* unit,
|
|
LLVMLocalScope* scope, LLVMBuilderRef builder,
|
|
Variable* variable, LLVMBool reference,
|
|
LLVMValueRef* llvm_result) {
|
|
|
|
LLVMValueRef llvm_variable = get_variable(scope, variable->name);
|
|
|
|
Type* type;
|
|
|
|
if (variable->kind == VariableKindDefinition) {
|
|
type = variable->impl.definiton.declaration.type;
|
|
} else {
|
|
type = variable->impl.declaration.type;
|
|
}
|
|
|
|
if (llvm_variable == NULL) {
|
|
return new_backend_impl_error(Implementation, NULL,
|
|
"Variable not found");
|
|
}
|
|
|
|
if (reference) {
|
|
// don't load in case variable is parameter?
|
|
// only reference wanted
|
|
|
|
*llvm_result = llvm_variable;
|
|
|
|
} else {
|
|
// no referencing, load value
|
|
LLVMTypeRef llvm_type;
|
|
|
|
get_type_impl(unit, scope->func_scope->global_scope, type, &llvm_type);
|
|
|
|
if (LLVMGetTypeKind(LLVMTypeOf(llvm_variable)) == LLVMPointerTypeKind) {
|
|
*llvm_result =
|
|
LLVMBuildLoad2(builder, llvm_type, llvm_variable, "");
|
|
} else {
|
|
*llvm_result = llvm_variable;
|
|
}
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
BackendError impl_parameter_load(LLVMBackendCompileUnit* unit,
|
|
LLVMLocalScope* scope, LLVMBuilderRef builder,
|
|
Parameter* parameter, LLVMBool reference,
|
|
LLVMValueRef* llvm_result) {
|
|
|
|
LLVMValueRef llvm_variable = NULL;
|
|
if (g_hash_table_contains(scope->func_scope->params, parameter->name)) {
|
|
llvm_variable =
|
|
g_hash_table_lookup(scope->func_scope->params, parameter->name);
|
|
}
|
|
|
|
Type* type;
|
|
|
|
ParameterDeclaration decl;
|
|
|
|
if (parameter->kind == ParameterDeclarationKind) {
|
|
decl = parameter->impl.definiton.declaration;
|
|
} else {
|
|
decl = parameter->impl.declaration;
|
|
}
|
|
type = decl.type;
|
|
|
|
if (llvm_variable == NULL) {
|
|
return new_backend_impl_error(Implementation, NULL,
|
|
"Variable not found");
|
|
}
|
|
|
|
if (decl.qualifier == In || reference) {
|
|
*llvm_result = llvm_variable;
|
|
} else {
|
|
// no referencing, load value
|
|
LLVMTypeRef llvm_type;
|
|
|
|
get_type_impl(unit, scope->func_scope->global_scope, type, &llvm_type);
|
|
|
|
if (LLVMGetTypeKind(LLVMTypeOf(llvm_variable)) == LLVMPointerTypeKind) {
|
|
*llvm_result =
|
|
LLVMBuildLoad2(builder, llvm_type, llvm_variable, "");
|
|
} else {
|
|
*llvm_result = llvm_variable;
|
|
}
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
BackendError impl_address_of(LLVMBackendCompileUnit* unit,
|
|
LLVMLocalScope* scope, LLVMBuilderRef builder,
|
|
AddressOf* addressOf, LLVMValueRef* llvm_result) {
|
|
|
|
BackendError err = impl_expr(unit, scope, builder, addressOf->variable,
|
|
TRUE, 0, llvm_result);
|
|
|
|
if (err.kind != Success) {
|
|
return err;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
BackendError impl_deref(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
|
LLVMBuilderRef builder, Dereference* dereference,
|
|
bool reference, uint32_t deref_depth,
|
|
LLVMValueRef* llvm_result) {
|
|
BackendError err;
|
|
|
|
LLVMValueRef llvm_pointer = NULL;
|
|
err = impl_expr(unit, scope, builder, dereference->variable, false,
|
|
deref_depth + 1, &llvm_pointer);
|
|
if (err.kind != Success) {
|
|
return err;
|
|
}
|
|
|
|
LLVMTypeRef llvm_deref_type = NULL;
|
|
err = get_type_impl(unit, scope->func_scope->global_scope,
|
|
dereference->variable->result->impl.reference,
|
|
&llvm_deref_type);
|
|
if (err.kind != Success) {
|
|
return err;
|
|
}
|
|
|
|
LLVMValueRef* index = mem_alloc(MemoryNamespaceLlvm, sizeof(LLVMValueRef));
|
|
err = impl_expr(unit, scope, builder, dereference->index, FALSE,
|
|
deref_depth + 1, index);
|
|
if (err.kind != Success) {
|
|
return err;
|
|
}
|
|
|
|
*llvm_result = LLVMBuildGEP2(builder, llvm_deref_type, llvm_pointer, index,
|
|
1, "expr.deref.gep2");
|
|
|
|
if (!reference || deref_depth > 0) {
|
|
*llvm_result = LLVMBuildLoad2(builder, llvm_deref_type, *llvm_result,
|
|
"expr.deref.load");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
BackendError impl_expr(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
|
LLVMBuilderRef builder, Expression* expr,
|
|
LLVMBool reference, uint32_t deref_depth,
|
|
LLVMValueRef* llvm_result) {
|
|
DEBUG("implementing expression: %ld", expr->kind);
|
|
BackendError err = SUCCESS;
|
|
|
|
switch (expr->kind) {
|
|
case ExpressionKindConstant:
|
|
err = get_const_type_value(unit, scope->func_scope->global_scope,
|
|
&expr->impl.constant, llvm_result);
|
|
break;
|
|
case ExpressionKindTransmute:
|
|
err = impl_transmute(unit, scope, builder, &expr->impl.transmute,
|
|
llvm_result);
|
|
break;
|
|
case ExpressionKindTypeCast:
|
|
err = impl_typecast(unit, scope, builder, &expr->impl.typecast,
|
|
reference, llvm_result);
|
|
break;
|
|
case ExpressionKindOperation:
|
|
err = impl_operation(unit, scope, builder, &expr->impl.operation,
|
|
llvm_result);
|
|
break;
|
|
case ExpressionKindVariable:
|
|
err = impl_variable_load(unit, scope, builder, expr->impl.variable,
|
|
reference, llvm_result);
|
|
break;
|
|
case ExpressionKindParameter:
|
|
err =
|
|
impl_parameter_load(unit, scope, builder, expr->impl.parameter,
|
|
reference, llvm_result);
|
|
break;
|
|
case ExpressionKindAddressOf:
|
|
err = impl_address_of(unit, scope, builder, &expr->impl.addressOf,
|
|
llvm_result);
|
|
break;
|
|
case ExpressionKindDereference:
|
|
err = impl_deref(unit, scope, builder, &expr->impl.dereference,
|
|
reference, deref_depth, llvm_result);
|
|
break;
|
|
case ExpressionKindFunctionCall:
|
|
err = impl_func_call(unit, builder, scope, expr->impl.call,
|
|
llvm_result);
|
|
break;
|
|
default:
|
|
err = new_backend_impl_error(Implementation, NULL,
|
|
"unknown expression");
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|