aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/llvm.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-07-27 14:06:42 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-07-27 14:19:53 -0700
commita8e964eadd3496330043985cacaaee7db92886c6 (patch)
tree57768a2f87b76b9f80231ab924d2bdb481a22d2a /src/codegen/llvm.zig
parentba71b96fe6c0e01b8445a2f7bd49541a07c360db (diff)
downloadzig-a8e964eadd3496330043985cacaaee7db92886c6.tar.gz
zig-a8e964eadd3496330043985cacaaee7db92886c6.zip
stage2: `zig test` now works with the LLVM backend
Frontend improvements: * When compiling in `zig test` mode, put a task on the work queue to analyze the main package root file. Normally, start code does `_ = import("root");` to make Zig analyze the user's code, however in the case of `zig test`, the root source file is the test runner. Without this change, no tests are picked up. * In the main pipeline, once semantic analysis is finished, if there are no compile errors, populate the `test_functions` Decl with the set of test functions picked up from semantic analysis. * Value: add `array` and `slice` Tags. LLVM backend improvements: * Fix incremental updates of globals. Previously the value of a global would not get replaced with a new value. * Fix LLVM type of arrays. They were incorrectly sending the ABI size as the element count. * Remove the FuncGen parameter from genTypedValue. This function is for generating global constants and there is no function available when it is being called. - The `ref_val` case is now commented out. I'd like to eliminate `ref_val` as one of the possible Value Tags. Instead it should always be done via `decl_ref`. * Implement constant value generation for slices, arrays, and structs. * Constant value generation for functions supports the `decl_ref` tag.
Diffstat (limited to 'src/codegen/llvm.zig')
-rw-r--r--src/codegen/llvm.zig134
1 files changed, 91 insertions, 43 deletions
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 1b3c36fc69..2468b1dbde 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -500,7 +500,18 @@ pub const DeclGen = struct {
} else if (decl.val.castTag(.extern_fn)) |extern_fn| {
_ = try self.resolveLlvmFunction(extern_fn.data);
} else {
- _ = try self.resolveGlobalDecl(decl);
+ const global = try self.resolveGlobalDecl(decl);
+ assert(decl.has_tv);
+ const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
+ const variable = payload.data;
+ break :init_val variable.init;
+ } else init_val: {
+ global.setGlobalConstant(.True);
+ break :init_val decl.val;
+ };
+
+ const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val });
+ llvm.setInitializer(global, llvm_init);
}
}
@@ -548,25 +559,11 @@ pub const DeclGen = struct {
}
fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
- if (self.llvmModule().getNamedGlobal(decl.name)) |val| return val;
-
- assert(decl.has_tv);
-
+ const llvm_module = self.object.llvm_module;
+ if (llvm_module.getNamedGlobal(decl.name)) |val| return val;
// TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.
const llvm_type = try self.llvmType(decl.ty);
- const global = self.llvmModule().addGlobal(llvm_type, decl.name);
- const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
- const variable = payload.data;
- break :init_val variable.init;
- } else init_val: {
- global.setGlobalConstant(.True);
- break :init_val decl.val;
- };
-
- const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val }, null);
- llvm.setInitializer(global, llvm_init);
-
- return global;
+ return llvm_module.addGlobal(llvm_type, decl.name);
}
fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
@@ -596,7 +593,8 @@ pub const DeclGen = struct {
},
.Array => {
const elem_type = try self.llvmType(t.elemType());
- return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
+ const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null);
+ return elem_type.arrayType(@intCast(c_uint, total_len));
},
.Optional => {
if (!t.isPtrLikeOptional()) {
@@ -674,8 +672,7 @@ pub const DeclGen = struct {
}
}
- // TODO: figure out a way to remove the FuncGen argument
- fn genTypedValue(self: *DeclGen, tv: TypedValue, fg: ?*FuncGen) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
+ fn genTypedValue(self: *DeclGen, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
const llvm_type = try self.llvmType(tv.ty);
if (tv.val.isUndef())
@@ -711,20 +708,36 @@ pub const DeclGen = struct {
usize_type.constNull(),
};
- // TODO: consider using buildInBoundsGEP2 for opaque pointers
- return fg.?.builder.buildInBoundsGEP(val, &indices, 2, "");
+ return val.constInBoundsGEP(&indices, indices.len);
},
.ref_val => {
- const elem_value = tv.val.castTag(.ref_val).?.data;
- const elem_type = tv.ty.castPointer().?.data;
- const alloca = fg.?.buildAlloca(try self.llvmType(elem_type));
- _ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca);
- return alloca;
+ //const elem_value = tv.val.castTag(.ref_val).?.data;
+ //const elem_type = tv.ty.castPointer().?.data;
+ //const alloca = fg.?.buildAlloca(try self.llvmType(elem_type));
+ //_ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca);
+ //return alloca;
+ // TODO eliminate the ref_val Value Tag
+ return self.todo("implement const of pointer tag ref_val", .{});
},
.variable => {
const variable = tv.val.castTag(.variable).?.data;
return self.resolveGlobalDecl(variable.owner_decl);
},
+ .slice => {
+ const slice = tv.val.castTag(.slice).?.data;
+ var buf: Type.Payload.ElemType = undefined;
+ const fields: [2]*const llvm.Value = .{
+ try self.genTypedValue(.{
+ .ty = tv.ty.slicePtrFieldType(&buf),
+ .val = slice.ptr,
+ }),
+ try self.genTypedValue(.{
+ .ty = Type.initTag(.usize),
+ .val = slice.len,
+ }),
+ };
+ return self.context.constStruct(&fields, fields.len, .False);
+ },
else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }),
},
.Array => {
@@ -734,10 +747,28 @@ pub const DeclGen = struct {
return self.todo("handle other sentinel values", .{});
} else false;
- return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel));
- } else {
- return self.todo("handle more array values", .{});
+ return self.context.constString(
+ payload.data.ptr,
+ @intCast(c_uint, payload.data.len),
+ llvm.Bool.fromBool(!zero_sentinel),
+ );
+ }
+ if (tv.val.castTag(.array)) |payload| {
+ const gpa = self.gpa;
+ const elem_ty = tv.ty.elemType();
+ const elem_vals = payload.data;
+ const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len);
+ defer gpa.free(llvm_elems);
+ for (elem_vals) |elem_val, i| {
+ llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_val });
+ }
+ const llvm_elem_ty = try self.llvmType(elem_ty);
+ return llvm_elem_ty.constArray(
+ llvm_elems.ptr,
+ @intCast(c_uint, llvm_elems.len),
+ );
}
+ return self.todo("handle more array values", .{});
},
.Optional => {
if (!tv.ty.isPtrLikeOptional()) {
@@ -750,26 +781,25 @@ pub const DeclGen = struct {
llvm_child_type.constNull(),
self.context.intType(1).constNull(),
};
- return self.context.constStruct(&optional_values, 2, .False);
+ return self.context.constStruct(&optional_values, optional_values.len, .False);
} else {
var optional_values: [2]*const llvm.Value = .{
- try self.genTypedValue(.{ .ty = child_type, .val = tv.val }, fg),
+ try self.genTypedValue(.{ .ty = child_type, .val = tv.val }),
self.context.intType(1).constAllOnes(),
};
- return self.context.constStruct(&optional_values, 2, .False);
+ return self.context.constStruct(&optional_values, optional_values.len, .False);
}
} else {
return self.todo("implement const of optional pointer", .{});
}
},
.Fn => {
- const fn_decl = if (tv.val.castTag(.extern_fn)) |extern_fn|
- extern_fn.data
- else if (tv.val.castTag(.function)) |func_payload|
- func_payload.data.owner_decl
- else
- unreachable;
-
+ const fn_decl = switch (tv.val.tag()) {
+ .extern_fn => tv.val.castTag(.extern_fn).?.data,
+ .function => tv.val.castTag(.function).?.data.owner_decl,
+ .decl_ref => tv.val.castTag(.decl_ref).?.data,
+ else => unreachable,
+ };
return self.resolveLlvmFunction(fn_decl);
},
.ErrorSet => {
@@ -793,11 +823,29 @@ pub const DeclGen = struct {
if (!payload_type.hasCodeGenBits()) {
// We use the error type directly as the type.
- return self.genTypedValue(.{ .ty = error_type, .val = sub_val }, fg);
+ return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
}
return self.todo("implement error union const of type '{}'", .{tv.ty});
},
+ .Struct => {
+ const fields_len = tv.ty.structFieldCount();
+ const field_vals = tv.val.castTag(.@"struct").?.data;
+ const gpa = self.gpa;
+ const llvm_fields = try gpa.alloc(*const llvm.Value, fields_len);
+ defer gpa.free(llvm_fields);
+ for (llvm_fields) |*llvm_field, i| {
+ llvm_field.* = try self.genTypedValue(.{
+ .ty = tv.ty.structFieldType(i),
+ .val = field_vals[i],
+ });
+ }
+ return self.context.constStruct(
+ llvm_fields.ptr,
+ @intCast(c_uint, llvm_fields.len),
+ .False,
+ );
+ },
else => return self.todo("implement const of type '{}'", .{tv.ty}),
}
}
@@ -869,7 +917,7 @@ pub const FuncGen = struct {
fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value {
if (self.air.value(inst)) |val| {
- return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val }, self);
+ return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val });
}
const inst_index = Air.refToIndex(inst).?;
if (self.func_inst_table.get(inst_index)) |value| return value;