diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-08-20 15:23:55 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-08-20 15:41:57 -0700 |
| commit | 0cd361219c107bce48f2d7b44c6f3dd05ea6ccf4 (patch) | |
| tree | c3de2205e0bb525d61ee78800c118b13efbf1029 /src/codegen/llvm.zig | |
| parent | 2f1abd919a8465e515875cd08816778df795aabe (diff) | |
| download | zig-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.zig | 35 |
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)) |
