Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array compound generation first pass #57

Open
wants to merge 11 commits into
base: rewrite
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/checker.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ Operand check_expr_compound(Checker *self, Expr *expr, Ty *wanted) {
index = (u32) op.val.i;
}
if (type && type->tarray.length && index >= type->tarray.length) {
error(self, field.key->range,
error(self, (field.key ?: field.val)->range,
vdka marked this conversation as resolved.
Show resolved Hide resolved
"Array index %lu is beyond the max index of %lu for type %s",
index, type->tarray.length, tyname(type));
}
Expand Down
182 changes: 105 additions & 77 deletions src/llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern "C" {
#include <llvm/IR/Constants.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Intrinsics.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
Expand Down Expand Up @@ -340,7 +341,8 @@ Type *llvm_type(IRContext *c, Ty *type, bool do_not_hide_behind_pointer = false)
case TYPE_ARRAY: {
Type *base = llvm_type(c, type->tarray.eltype);
Type *ty = ArrayType::get(base, type->tarray.length);
return ty;
if (do_not_hide_behind_pointer) return ty;
return PointerType::get(ty, 0);
}
case TYPE_SLICE: {
if (type == type_string) return c->ty.rawptr; // FIXME: Temp hack while we don't have slices
Expand Down Expand Up @@ -452,7 +454,7 @@ DIType *llvm_debug_type(IRContext *c, Ty *type, bool do_not_hide_behind_pointer
subscripts.push_back(c->dbg.builder->getOrCreateSubrange(0, eltype->tarray.length));
eltype = eltype->tarray.eltype;
}
Type *irtype = llvm_type(c, type);
Type *irtype = llvm_type(c, type, true);
u64 size = c->data_layout.getTypeSizeInBits(irtype);
u32 align = c->data_layout.getPrefTypeAlignment(irtype) * 8;
DIType *deltype = llvm_debug_type(c, type->tarray.eltype);
Expand Down Expand Up @@ -731,8 +733,12 @@ Value *create_coerce(IRContext *self, Value *val, Expr *expr, bool is_lvalue = f
val = self->builder.CreatePtrToInt(val, self->ty.intptr);
goto start;
}
if (isa<FunctionType>(val_ty)) return val;
if (val_ty->getPointerElementType() == dst_ty) return create_load(self, val); // FIXME: Do not do this....

if (isa<FunctionType>(val_ty))
return val;

if (val_ty->getPointerElementType() == dst_ty)
return create_load(self, val); // FIXME: Do not do this....
// FIXME: IF isa<ArrayType> What do?
return self->builder.CreatePointerBitCastOrAddrSpaceCast(val, dst_ty);
}
Expand Down Expand Up @@ -905,29 +911,21 @@ IRValue emit_expr_compound(IRContext *self, Expr *expr) {
fatal("Unimplemented");
case TYPE_ARRAY: {
Type *eltype = llvm_type(self, operand.type->tarray.eltype);
ArrayType *type = (ArrayType *) llvm_type(self, operand.type);
Value *agg = Constant::getNullValue(type);
u32 index = 0;
for (i64 i = 0; i < arrlen(expr->ecompound.fields); i++) {
Value *val = emit_expr(self, expr->ecompound.fields[i].val).val;
if (expr->ecompound.fields[i].key) {
Operand op = hmget(self->package->operands, expr->ecompound.fields[i].key);
index = (u32) op.val.u;
}
agg = self->builder.CreateInsertValue(agg, val, index);
index++;
}
return irval(agg);
ArrayType *type = (ArrayType *) llvm_type(self, operand.type, true);


Value *value;
if (!expr->ecompound.fields) return irval(value);
Value *agg = Constant::getNullValue(type);

if (!expr->ecompound.fields)
return irval(agg);

bool is_all_members_constant = true;
bool does_have_gaps = operand.type->tarray.length != arrlen(expr->ecompound.fields);

std::vector<Value *> values;
std::vector<Constant *> constants;
for (i64 i = 0; i < arrlen(expr->ecompound.fields); i++) {
Value *el = emit_expr(self, expr->ecompound.fields[i].val).val;
is_all_members_constant |= isa<Constant>(el);
is_all_members_constant &= isa<Constant>(el);
if (Constant *constant = dyn_cast<Constant>(el)) {
constants.push_back(constant);
} else {
Expand All @@ -936,45 +934,61 @@ IRValue emit_expr_compound(IRContext *self, Expr *expr) {
}
values.push_back(el);
}

if (!self->fn) {
return irval(llvm::ConstantArray::get(type, constants));
}

u32 alignment = self->data_layout.getPrefTypeAlignment(type);
// FIXME: can't do this if we aren't in a function.
Constant *constant = ConstantArray::get(type, constants);
return irval(constant);

if (constant->isZeroValue()) {
GlobalVariable *global = new GlobalVariable(
*self->module, type, true, GlobalValue::PrivateLinkage, constant,
"compound.lit");
return irval(global);
}

AllocaInst *alloca = emit_entry_alloca(self, type, "compound.lit", alignment);
u32 size = (u32)self->data_layout.getTypeStoreSize(type);

AllocaInst *alloca = emit_entry_alloca(self, type, "compound.lit", alignment);

if (is_all_members_constant) {
u64 target_index = 0;
for (i64 i = 0; i < arrlen(expr->ecompound.fields); i++) {
CompoundField field = expr->ecompound.fields[i];
u64 target_index = hmget(self->package->operands, field.key).val.u;
value = self->builder.CreateInsertValue(value, values[i], {(u32) target_index});
}
if (isa<Constant>(value)) {
GlobalVariable *global = new GlobalVariable(
*self->module, type, true, GlobalValue::PrivateLinkage, (Constant *)value,
"compound.lit");
self->builder.CreateMemCpy(
alloca, alignment, global, global->getAlignment(),
type->getPrimitiveSizeInBits() / 8);
value = alloca;
} else {
// create_store(self, value, alloca);
if (field.kind == FIELD_INDEX) {
target_index = (u32) hmget(self->package->operands, field.key).val.u;
}

agg = self->builder.CreateInsertValue(agg, constants[i], {(u32)target_index});
target_index++;
}

GlobalVariable *global = new GlobalVariable(
*self->module, type, true, GlobalValue::PrivateLinkage, (Constant *)agg,
"compound.lit");

self->builder.CreateMemCpy(
alloca, alignment, global, global->getAlignment(), size
);
} else {
// NOTE: memset requires `zero` to be a char
llvm::Value *zero = llvm::ConstantInt::get(self->ty.i8, 0);
llvm::Value *element = self->builder.CreateInBoundsGEP(alloca, {zero, zero});

if (does_have_gaps) {
self->builder.CreateMemSet(alloca, zero, size, alignment);
}

u64 target_index = 0;

for (i64 i = 0; i < arrlen(expr->ecompound.fields); i++) {
CompoundField field = expr->ecompound.fields[i];
u64 target_index = hmget(self->package->operands, field.key).val.u;
value = self->builder.CreateInsertValue(
alloca, values[i], {0, (u32) target_index});
if (field.kind == FIELD_INDEX) {
target_index = (u32) hmget(self->package->operands, field.key).val.u;
}

llvm::Value *offset = llvm::ConstantInt::get(self->ty.i64, target_index);
self->builder.CreateInBoundsGEP(element, offset);
create_store(self, values[i], element);

target_index++;
}
}
return irval(alloca);

return irval(alloca, true);
}
case TYPE_SLICE:
default:
Expand Down Expand Up @@ -1339,7 +1353,10 @@ IRValue emit_expr(IRContext *self, Expr *expr, bool is_lvalue) {
default:
fatal("Unrecognized ExprKind %s", describe_ast_kind(expr->kind));
}
if (is_lvalue) val.val = remove_load(self, val.val);

if (is_lvalue)
val.val = remove_load(self, val.val);

if (is_lvalue && !isa<PointerType>(val.val->getType())) {
// make the val into an lvalue using a temp alloca
u32 alignment = self->data_layout.getPrefTypeAlignment(val.val->getType());
Expand Down Expand Up @@ -1732,16 +1749,26 @@ void emit_decl_var(IRContext *self, Decl *decl) {
return;
}
}
Type *type = llvm_type(self, sym->type);

Type *type = llvm_type(self, sym->type, true);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like I need the true here for global variables but I'm not exactly sure if I need it for all cases. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense for array's, a Global Variable needs the raw type so it know's how much space to reserve, this actually will cause an error for functions though, llvm_type will return a FunctionType for functions, this will cause an assert in LLVM when it attempts to work out a size for the global.
I guess maybe we need llvm_type's hide behind pointer to be a mask maybe? Something like llvm_type(self, sym->type, ARRAY | STRUCT) and in emit_expr_func we would have llvm_type(self, type, FUNCTION)


Value *alloca;
if (rhs_is_alloca) {
alloca = rhs;
} else {
// FIXME: Alloca can't happen at global scope instead use a global variable and add
// check that we only initialize with global variables.
alloca = emit_entry_alloca(self, type, sym->name, sym->type->align);
if (rhs) create_coerced_store(self, rhs, alloca);
if (self->fn) {
alloca = emit_entry_alloca(self, type, sym->name, sym->type->align);
if (rhs) create_coerced_store(self, rhs, alloca);
} else {
GlobalVariable *global = new GlobalVariable(
*self->module, type, false, GlobalValue::ExternalLinkage, (Constant *)rhs,
sym->external_name ?: sym->name);
global->setAlignment(sym->type->align);
global->setExternallyInitialized(false);
alloca = global;
}
}
sym->userdata = alloca;
set_debug_pos(self, name->range);
Expand Down Expand Up @@ -2002,29 +2029,30 @@ bool llvm_emit_object(Package *package) {
legacy::PassManager module_pm;
pm_builder->populateModulePassManager(module_pm);

module_pm.add(createBasicAAWrapperPass());
module_pm.add(createInstructionCombiningPass());
module_pm.add(createAggressiveDCEPass());
module_pm.add(createReassociatePass());
module_pm.add(createPromoteMemoryToRegisterPass());

if (!compiler.flags.disable_all_passes) {
TargetMachine::CodeGenFileType file_type = TargetMachine::CGFT_ObjectFile;
if (self->target->addPassesToEmitFile(module_pm, dest, nullptr, file_type)) {
errs() << "TargetMachine cannot emit a file of this type";
return false;
}

// run per function optimization passes
function_pm.doInitialization();
for (Function &function : *self->module)
if (!function.isDeclaration())
function_pm.run(function);
function_pm.doFinalization();

module_pm.run(*self->module);
}
dest.flush();
module_pm.add(createBasicAAWrapperPass());
module_pm.add(createInstructionCombiningPass());
module_pm.add(createAggressiveDCEPass());
module_pm.add(createReassociatePass());
module_pm.add(createPromoteMemoryToRegisterPass());

if (!compiler.flags.disable_all_passes) {
TargetMachine::CodeGenFileType file_type = TargetMachine::CGFT_ObjectFile;
if (self->target->addPassesToEmitFile(module_pm, dest, nullptr, file_type)) {
errs() << "TargetMachine cannot emit a file of this type";
return false;
}

// run per function optimization passes
function_pm.doInitialization();
for (Function &function : *self->module)
if (!function.isDeclaration())
function_pm.run(function);
function_pm.doFinalization();

module_pm.run(*self->module);
}

dest.flush();

if (compiler.flags.dump_ir) {
std::string buf;
Expand Down
10 changes: 10 additions & 0 deletions test/array.kai
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
testArray := [3]i32 { 1, 2, 3 }

test :: fn(b: i32) -> void {
a := [..]i32 { 0, 1, 2, 3}
}

main :: fn() -> void {
a := 11
b := a + 11
}