aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/llvm.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-08-20 15:23:55 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-08-20 15:41:57 -0700
commit0cd361219c107bce48f2d7b44c6f3dd05ea6ccf4 (patch)
treec3de2205e0bb525d61ee78800c118b13efbf1029 /src/codegen/llvm.zig
parent2f1abd919a8465e515875cd08816778df795aabe (diff)
downloadzig-0cd361219c107bce48f2d7b44c6f3dd05ea6ccf4.tar.gz
zig-0cd361219c107bce48f2d7b44c6f3dd05ea6ccf4.zip
stage2: field type expressions support referencing locals
The big change in this commit is making `semaDecl` resolve the fields if the Decl ends up being a struct or union. It needs to do this while the `Sema` is still in scope, because it will have the resolved AIR instructions that the field type expressions possibly reference. We do this after the decl is populated and set to `complete` so that a `Decl` may reference itself. Everything else is fixes and improvements to make the test suite pass again after making this change. * New AIR instruction: `ptr_elem_ptr` - Implemented for LLVM backend * New Type tag: `type_info` which represents `std.builtin.TypeInfo`. It is used by AstGen for the operand type of `@Type`. * ZIR instruction `set_float_mode` uses `coerced_ty` to avoid superfluous `as` instruction on operand. * ZIR instruction `Type` uses `coerced_ty` to properly handle result location type of operand. * Fix two instances of `enum_nonexhaustive` Value Tag not handled properly - it should generally be handled the same as `enum_full`. * Fix struct and union field resolution not copying Type and Value objects into its Decl arena. * Fix enum tag value resolution discarding the ZIR=>AIR instruction map for the child Sema, when they still needed to be accessed. * Fix `zirResolveInferredAlloc` use-after-free in the AIR instructions data array. * Fix `elemPtrArray` not respecting const/mutable attribute of pointer in the result type. * Fix LLVM backend crashing when `updateDeclExports` is called before `updateDecl`/`updateFunc` (which is, according to the API, perfectly legal for the frontend to do). * Fix LLVM backend handling element pointer of pointer-to-array. It needed another index in the GEP otherwise LLVM saw the wrong type. * Fix LLVM test cases not returning 0 from main, causing test failures. Fixes a regression introduced in 6a5094872f10acc629543cc7f10533b438d0283a. * Implement comptime shift-right. * Implement `@Type` for integers and `@TypeInfo` for integers. * Implement union initialization syntax. * Implement `zirFieldType` for unions. * Implement `elemPtrArray` for a runtime-known operand. * Make `zirLog2IntType` support RHS of shift being `comptime_int`. In this case it returns `comptime_int`. The motivating test case for this commit was originally: ```zig test "example" { var l: List(10) = undefined; l.array[1] = 1; } fn List(comptime L: usize) type { var T = u8; return struct { array: [L]T, }; } ``` However I changed it to: ```zig test "example" { var l: List = undefined; l.array[1] = 1; } const List = blk: { const T = [10]u8; break :blk struct { array: T, }; }; ``` Which ended up being a similar, smaller problem. The former test case will require a similar solution in the implementation of comptime function calls - checking if the result of the function call is a struct or union, and using the child `Sema` before it is destroyed to resolve the fields.
Diffstat (limited to 'src/codegen/llvm.zig')
-rw-r--r--src/codegen/llvm.zig35
1 files changed, 32 insertions, 3 deletions
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 632b275704..745fb036db 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -432,6 +432,8 @@ pub const Object = struct {
},
else => |e| return e,
};
+ const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
+ try self.updateDeclExports(module, decl, decl_exports);
}
pub fn updateDeclExports(
@@ -440,7 +442,9 @@ pub const Object = struct {
decl: *const Module.Decl,
exports: []const *Module.Export,
) !void {
- const llvm_fn = self.llvm_module.getNamedFunction(decl.name).?;
+ // If the module does not already have the function, we ignore this function call
+ // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`.
+ const llvm_fn = self.llvm_module.getNamedFunction(decl.name) orelse return;
const is_extern = decl.val.tag() == .extern_fn;
if (is_extern or exports.len != 0) {
llvm_fn.setLinkage(.External);
@@ -1041,6 +1045,7 @@ pub const FuncGen = struct {
.slice_elem_val => try self.airSliceElemVal(inst),
.ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
.ptr_elem_val => try self.airPtrElemVal(inst),
+ .ptr_elem_ptr => try self.airPtrElemPtr(inst),
.ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst),
.optional_payload => try self.airOptionalPayload(inst, false),
@@ -1296,11 +1301,35 @@ pub const FuncGen = struct {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const base_ptr = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
- const indices: [1]*const llvm.Value = .{rhs};
- const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ const ptr = if (self.air.typeOf(bin_op.lhs).isSinglePointer()) ptr: {
+ // If this is a single-item pointer to an array, we need another index in the GEP.
+ const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
+ break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ } else ptr: {
+ const indices: [1]*const llvm.Value = .{rhs};
+ break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ };
return self.builder.buildLoad(ptr, "");
}
+ fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst))
+ return null;
+
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
+ const base_ptr = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ if (self.air.typeOf(bin_op.lhs).isSinglePointer()) {
+ // If this is a single-item pointer to an array, we need another index in the GEP.
+ const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
+ return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ } else {
+ const indices: [1]*const llvm.Value = .{rhs};
+ return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ }
+ }
+
fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
const is_volatile = false; // TODO
if (!is_volatile and self.liveness.isUnused(inst))