From 31a59c229cb39b9ffd1ee3397a1ce87c36b91477 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 26 Jul 2021 19:12:34 -0700 Subject: [PATCH] stage2: improvements towards `zig test` * Add AIR instruction: struct_field_val - This is part of an effort to eliminate the AIR instruction `ref`. - It's implemented for C backend and LLVM backend so far. * Rename `resolvePossiblyUndefinedValue` to `resolveMaybeUndefVal` just to save some columns on long lines. * Sema: add `fieldVal` alongside `fieldPtr` (renamed from `namedFieldPtr`). This is part of an effort to eliminate the AIR instruction `ref`. The idea is to avoid unnecessary loads, stores, stack usage, and IR instructions, by paying a DRY cost. LLVM backend improvements: * internal linkage vs exported linkage is implemented, along with aliases. There is an issue with incremental updates due to missing LLVM API for deleting aliases; see the relevant comment in this commit. - `updateDeclExports` is hooked up to the LLVM backend now. * Fix usage of `Type.tag() == .noreturn` rather than calling `isNoReturn()`. * Properly mark global variables as mutable/constant. * Fix llvm type generation of function pointers * Fix codegen for calls of function pointers * Implement llvm type generation of error unions and error sets. * Implement AIR instructions: addwrap, subwrap, mul, mulwrap, div, bit_and, bool_and, bit_or, bool_or, xor, struct_field_ptr, struct_field_val, unwrap_errunion_err, add for floats, sub for floats. After this commit, `zig test` on a file with `test "example" {}` correctly generates and executes a test binary. However the `test_functions` slice is undefined and just happens to be going into the .bss section, causing the length to be 0. The next step towards `zig test` will be replacing the `test_functions` Decl Value with the set of test function pointers, before it is sent to linker/codegen. --- src/Air.zig | 7 +- src/Liveness.zig | 4 +- src/Module.zig | 4 +- src/Sema.zig | 386 ++++++++++++++++++++++++++++------ src/codegen.zig | 9 + src/codegen/c.zig | 25 ++- src/codegen/llvm.zig | 362 ++++++++++++++++++++++++------- src/codegen/llvm/bindings.zig | 133 +++++++++++- src/codegen/wasm.zig | 2 +- src/link/Coff.zig | 14 +- src/link/Elf.zig | 7 +- src/link/MachO.zig | 6 + src/link/Wasm.zig | 10 +- src/print_air.zig | 7 +- 14 files changed, 808 insertions(+), 168 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 9451f8b763..8cb7b943e3 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -247,6 +247,9 @@ pub const Inst = struct { /// Given a pointer to a struct and a field index, returns a pointer to the field. /// Uses the `ty_pl` field, payload is `StructField`. struct_field_ptr, + /// Given a byval struct and a field index, returns the field byval. + /// Uses the `ty_pl` field, payload is `StructField`. + struct_field_val, /// Given a slice value, return the length. /// Result type is always usize. /// Uses the `ty_op` field. @@ -376,7 +379,8 @@ pub const SwitchBr = struct { }; pub const StructField = struct { - struct_ptr: Inst.Ref, + /// Whether this is a pointer or byval is determined by the AIR tag. + struct_operand: Inst.Ref, field_index: u32, }; @@ -448,6 +452,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .constant, .varptr, .struct_field_ptr, + .struct_field_val, => return air.getRefType(datas[inst].ty_pl.ty), .not, diff --git a/src/Liveness.zig b/src/Liveness.zig index c6617e9b29..a4c6d8c016 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -320,9 +320,9 @@ fn analyzeInst( } return extra_tombs.finish(); }, - .struct_field_ptr => { + .struct_field_ptr, .struct_field_val => { const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data; - return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_ptr, .none, .none }); + return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none }); }, .br => { const br = inst_datas[inst].br; diff --git a/src/Module.zig b/src/Module.zig index 8ebb1c203c..99f314c5cb 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3702,8 +3702,8 @@ pub fn analyzeExport( else => return mod.fail(scope, src, "unable to export type '{}'", .{exported_decl.ty}), } - try mod.decl_exports.ensureCapacity(mod.gpa, mod.decl_exports.count() + 1); - try mod.export_owners.ensureCapacity(mod.gpa, mod.export_owners.count() + 1); + try mod.decl_exports.ensureUnusedCapacity(mod.gpa, 1); + try mod.export_owners.ensureUnusedCapacity(mod.gpa, 1); const new_export = try mod.gpa.create(Export); errdefer mod.gpa.destroy(new_export); diff --git a/src/Sema.zig b/src/Sema.zig index 67cfc09b53..46a41426c8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -655,7 +655,7 @@ fn resolveDefinedValue( src: LazySrcLoc, air_ref: Air.Inst.Ref, ) CompileError!?Value { - if (try sema.resolvePossiblyUndefinedValue(block, src, air_ref)) |val| { + if (try sema.resolveMaybeUndefVal(block, src, air_ref)) |val| { if (val.isUndef()) { return sema.failWithUseOfUndef(block, src); } @@ -664,7 +664,7 @@ fn resolveDefinedValue( return null; } -fn resolvePossiblyUndefinedValue( +fn resolveMaybeUndefVal( sema: *Sema, block: *Scope.Block, src: LazySrcLoc, @@ -1293,7 +1293,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co }; return sema.mod.failWithOwnedErrorMsg(&block.base, msg); } - const result_ptr = try sema.namedFieldPtr(block, src, array_ptr, "len", src); + const result_ptr = try sema.fieldPtr(block, src, array_ptr, "len", src); const result_ptr_src = array_ptr_src; return sema.analyzeLoad(block, src, result_ptr, result_ptr_src); } @@ -1789,7 +1789,7 @@ fn zirCompileLog( const arg = sema.resolveInst(arg_ref); const arg_ty = sema.typeOf(arg); - if (try sema.resolvePossiblyUndefinedValue(block, src, arg)) |val| { + if (try sema.resolveMaybeUndefVal(block, src, arg)) |val| { try writer.print("@as({}, {})", .{ arg_ty, val }); } else { try writer.print("@as({}, [runtime value])", .{arg_ty}); @@ -2579,7 +2579,7 @@ fn zirErrorToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src); const result_ty = Type.initTag(.u16); - if (try sema.resolvePossiblyUndefinedValue(block, src, op_coerced)) |val| { + if (try sema.resolveMaybeUndefVal(block, src, op_coerced)) |val| { if (val.isUndef()) { return sema.addConstUndef(result_ty); } @@ -2759,7 +2759,7 @@ fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE return sema.addConstant(int_tag_ty, opv); } - if (try sema.resolvePossiblyUndefinedValue(block, operand_src, enum_tag)) |enum_tag_val| { + if (try sema.resolveMaybeUndefVal(block, operand_src, enum_tag)) |enum_tag_val| { if (enum_tag_val.castTag(.enum_field_index)) |enum_field_payload| { const field_index = enum_field_payload.data; switch (enum_tag_ty.tag()) { @@ -2806,7 +2806,7 @@ fn zirIntToEnum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE return mod.fail(&block.base, dest_ty_src, "expected enum, found {}", .{dest_ty}); } - if (try sema.resolvePossiblyUndefinedValue(block, operand_src, operand)) |int_val| { + if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |int_val| { if (dest_ty.isNonexhaustiveEnum()) { return sema.addConstant(dest_ty, int_val); } @@ -3309,16 +3309,16 @@ fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; + const lhs_src: LazySrcLoc = src; // TODO const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; const field_name = sema.code.nullTerminatedString(extra.field_name_start); const object = sema.resolveInst(extra.lhs); - const object_ptr = if (sema.typeOf(object).zigTypeTag() == .Pointer) - object - else - try sema.analyzeRef(block, src, object); - const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); - const result_ptr_src = src; - return sema.analyzeLoad(block, src, result_ptr, result_ptr_src); + if (sema.typeOf(object).isSinglePointer()) { + const result_ptr = try sema.fieldPtr(block, src, object, field_name, field_name_src); + return sema.analyzeLoad(block, src, result_ptr, lhs_src); + } else { + return sema.fieldVal(block, src, object, field_name, field_name_src); + } } fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -3331,7 +3331,7 @@ fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; const field_name = sema.code.nullTerminatedString(extra.field_name_start); const object_ptr = sema.resolveInst(extra.lhs); - return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); + return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); } fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -3344,9 +3344,7 @@ fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; const object = sema.resolveInst(extra.lhs); const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); - const object_ptr = try sema.analyzeRef(block, src, object); - const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); - return sema.analyzeLoad(block, src, result_ptr, src); + return sema.fieldVal(block, src, object, field_name, field_name_src); } fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -3359,7 +3357,7 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; const object_ptr = sema.resolveInst(extra.lhs); const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); - return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); + return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); } fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -4691,8 +4689,8 @@ fn zirBitwise( return sema.mod.fail(&block.base, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag()), @tagName(rhs_ty.zigTypeTag()) }); } - if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, casted_lhs)) |lhs_val| { - if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, casted_rhs)) |rhs_val| { + if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { if (lhs_val.isUndef() or rhs_val.isUndef()) { return sema.addConstUndef(resolved_type); } @@ -4823,8 +4821,8 @@ fn analyzeArithmetic( return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag()), @tagName(rhs_ty.zigTypeTag()) }); } - if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, casted_lhs)) |lhs_val| { - if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, casted_rhs)) |rhs_val| { + if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { if (lhs_val.isUndef() or rhs_val.isUndef()) { return sema.addConstUndef(resolved_type); } @@ -5038,8 +5036,8 @@ fn zirCmp( if (!is_equality_cmp) { return mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)}); } - if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, lhs)) |lval| { - if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, rhs)) |rval| { + if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lval| { + if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rval| { if (lval.isUndef() or rval.isUndef()) { return sema.addConstUndef(Type.initTag(.bool)); } @@ -5085,8 +5083,8 @@ fn zirCmp( const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, casted_lhs)) |lhs_val| { - if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, casted_rhs)) |rhs_val| { + if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { if (lhs_val.isUndef() or rhs_val.isUndef()) { return sema.addConstUndef(resolved_type); } @@ -5759,7 +5757,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: if (is_comptime) { const values = try sema.arena.alloc(Value, field_inits.len); for (field_inits) |field_init, i| { - values[i] = (sema.resolvePossiblyUndefinedValue(block, src, field_init) catch unreachable).?; + values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; } return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr)); } @@ -6234,7 +6232,7 @@ fn zirVarExtended( const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; const init_air_inst = sema.resolveInst(init_ref); - break :blk (try sema.resolvePossiblyUndefinedValue(block, init_src, init_air_inst)) orelse + break :blk (try sema.resolveMaybeUndefVal(block, init_src, init_air_inst)) orelse return sema.failWithNeededComptime(block, init_src); } else Value.initTag(.unreachable_value); @@ -6565,7 +6563,177 @@ fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { } } -fn namedFieldPtr( +fn fieldVal( + sema: *Sema, + block: *Scope.Block, + src: LazySrcLoc, + object: Air.Inst.Ref, + field_name: []const u8, + field_name_src: LazySrcLoc, +) CompileError!Air.Inst.Ref { + // When editing this function, note that there is corresponding logic to be edited + // in `fieldPtr`. This function takes a value and returns a value. + + const mod = sema.mod; + const arena = sema.arena; + const object_src = src; // TODO better source location + const object_ty = sema.typeOf(object); + + switch (object_ty.zigTypeTag()) { + .Array => { + if (mem.eql(u8, field_name, "len")) { + return sema.addConstant( + Type.initTag(.comptime_int), + try Value.Tag.int_u64.create(arena, object_ty.arrayLen()), + ); + } else { + return mod.fail( + &block.base, + field_name_src, + "no member named '{s}' in '{}'", + .{ field_name, object_ty }, + ); + } + }, + .Pointer => switch (object_ty.ptrSize()) { + .Slice => { + if (mem.eql(u8, field_name, "ptr")) { + const buf = try arena.create(Type.Payload.ElemType); + const result_ty = object_ty.slicePtrFieldType(buf); + if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| { + if (val.isUndef()) return sema.addConstUndef(result_ty); + return mod.fail( + &block.base, + field_name_src, + "TODO implement comptime slice ptr", + .{}, + ); + } + try sema.requireRuntimeBlock(block, src); + return block.addTyOp(.slice_ptr, result_ty, object); + } else if (mem.eql(u8, field_name, "len")) { + const result_ty = Type.initTag(.usize); + if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| { + if (val.isUndef()) return sema.addConstUndef(result_ty); + return sema.addConstant( + result_ty, + try Value.Tag.int_u64.create(arena, val.sliceLen()), + ); + } + try sema.requireRuntimeBlock(block, src); + return block.addTyOp(.slice_len, result_ty, object); + } else { + return mod.fail( + &block.base, + field_name_src, + "no member named '{s}' in '{}'", + .{ field_name, object_ty }, + ); + } + }, + .One => { + const elem_ty = object_ty.elemType(); + if (elem_ty.zigTypeTag() == .Array) { + if (mem.eql(u8, field_name, "len")) { + return sema.addConstant( + Type.initTag(.comptime_int), + try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()), + ); + } else { + return mod.fail( + &block.base, + field_name_src, + "no member named '{s}' in '{}'", + .{ field_name, object_ty }, + ); + } + } + }, + .Many, .C => {}, + }, + .Type => { + const val = (try sema.resolveDefinedValue(block, object_src, object)).?; + const child_type = try val.toType(arena); + switch (child_type.zigTypeTag()) { + .ErrorSet => { + // TODO resolve inferred error sets + const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: { + const error_set = payload.data; + // TODO this is O(N). I'm putting off solving this until we solve inferred + // error sets at the same time. + const names = error_set.names_ptr[0..error_set.names_len]; + for (names) |name| { + if (mem.eql(u8, field_name, name)) { + break :blk name; + } + } + return mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{ + field_name, child_type, + }); + } else (try mod.getErrorValue(field_name)).key; + + return sema.addConstant( + child_type, + try Value.Tag.@"error".create(arena, .{ .name = name }), + ); + }, + .Struct, .Opaque, .Union => { + if (child_type.getNamespace()) |namespace| { + if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { + return sema.analyzeLoad(block, src, inst, src); + } + } + // TODO add note: declared here + const kw_name = switch (child_type.zigTypeTag()) { + .Struct => "struct", + .Opaque => "opaque", + .Union => "union", + else => unreachable, + }; + return mod.fail(&block.base, src, "{s} '{}' has no member named '{s}'", .{ + kw_name, child_type, field_name, + }); + }, + .Enum => { + if (child_type.getNamespace()) |namespace| { + if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { + return sema.analyzeLoad(block, src, inst, src); + } + } + const field_index = child_type.enumFieldIndex(field_name) orelse { + const msg = msg: { + const msg = try mod.errMsg( + &block.base, + src, + "enum '{}' has no member named '{s}'", + .{ child_type, field_name }, + ); + errdefer msg.destroy(sema.gpa); + try mod.errNoteNonLazy( + child_type.declSrcLoc(), + msg, + "enum declared here", + .{}, + ); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(&block.base, msg); + }; + const field_index_u32 = @intCast(u32, field_index); + const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32); + return sema.addConstant(child_type, enum_val); + }, + else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}), + } + }, + .Struct => return sema.structFieldVal(block, src, object, field_name, field_name_src, object_ty), + .Union => return sema.unionFieldVal(block, src, object, field_name, field_name_src, object_ty), + else => {}, + } + return mod.fail(&block.base, src, "type '{}' does not support field access", .{object_ty}); +} + +fn fieldPtr( sema: *Sema, block: *Scope.Block, src: LazySrcLoc, @@ -6573,23 +6741,25 @@ fn namedFieldPtr( field_name: []const u8, field_name_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + // When editing this function, note that there is corresponding logic to be edited + // in `fieldVal`. This function takes a pointer and returns a pointer. + const mod = sema.mod; const arena = sema.arena; - const object_ptr_src = src; // TODO better source location const object_ptr_ty = sema.typeOf(object_ptr); - const elem_ty = switch (object_ptr_ty.zigTypeTag()) { + const object_ty = switch (object_ptr_ty.zigTypeTag()) { .Pointer => object_ptr_ty.elemType(), else => return mod.fail(&block.base, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty}), }; - switch (elem_ty.zigTypeTag()) { + switch (object_ty.zigTypeTag()) { .Array => { if (mem.eql(u8, field_name, "len")) { return sema.addConstant( Type.initTag(.single_const_pointer_to_comptime_int), try Value.Tag.ref_val.create( arena, - try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()), + try Value.Tag.int_u64.create(arena, object_ty.arrayLen()), ), ); } else { @@ -6597,33 +6767,33 @@ fn namedFieldPtr( &block.base, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, elem_ty }, + .{ field_name, object_ty }, ); } }, .Pointer => { - const ptr_child = elem_ty.elemType(); + const ptr_child = object_ty.elemType(); if (ptr_child.isSlice()) { if (mem.eql(u8, field_name, "ptr")) { return mod.fail( &block.base, field_name_src, "cannot obtain reference to pointer field of slice '{}'", - .{elem_ty}, + .{object_ty}, ); } else if (mem.eql(u8, field_name, "len")) { return mod.fail( &block.base, field_name_src, "cannot obtain reference to length field of slice '{}'", - .{elem_ty}, + .{object_ty}, ); } else { return mod.fail( &block.base, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, elem_ty }, + .{ field_name, object_ty }, ); } } else switch (ptr_child.zigTypeTag()) { @@ -6641,7 +6811,7 @@ fn namedFieldPtr( &block.base, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, elem_ty }, + .{ field_name, object_ty }, ); } }, @@ -6684,7 +6854,7 @@ fn namedFieldPtr( }, .Struct, .Opaque, .Union => { if (child_type.getNamespace()) |namespace| { - if (try sema.analyzeNamespaceLookup(block, src, namespace, field_name)) |inst| { + if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { return inst; } } @@ -6701,7 +6871,7 @@ fn namedFieldPtr( }, .Enum => { if (child_type.getNamespace()) |namespace| { - if (try sema.analyzeNamespaceLookup(block, src, namespace, field_name)) |inst| { + if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { return inst; } } @@ -6734,20 +6904,20 @@ fn namedFieldPtr( else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}), } }, - .Struct => return sema.analyzeStructFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty), - .Union => return sema.analyzeUnionFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty), + .Struct => return sema.structFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty), + .Union => return sema.unionFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty), else => {}, } - return mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty}); + return mod.fail(&block.base, src, "type '{}' does not support field access", .{object_ty}); } -fn analyzeNamespaceLookup( +fn namespaceLookup( sema: *Sema, block: *Scope.Block, src: LazySrcLoc, namespace: *Scope.Namespace, decl_name: []const u8, -) CompileError!?Air.Inst.Ref { +) CompileError!?*Decl { const mod = sema.mod; const gpa = sema.gpa; if (try sema.lookupInNamespace(namespace, decl_name)) |decl| { @@ -6762,12 +6932,23 @@ fn analyzeNamespaceLookup( }; return mod.failWithOwnedErrorMsg(&block.base, msg); } - return try sema.analyzeDeclRef(block, src, decl); + return decl; } return null; } -fn analyzeStructFieldPtr( +fn namespaceLookupRef( + sema: *Sema, + block: *Scope.Block, + src: LazySrcLoc, + namespace: *Scope.Namespace, + decl_name: []const u8, +) CompileError!?Air.Inst.Ref { + const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null; + return try sema.analyzeDeclRef(block, src, decl); +} + +fn structFieldPtr( sema: *Sema, block: *Scope.Block, src: LazySrcLoc, @@ -6803,14 +6984,52 @@ fn analyzeStructFieldPtr( .data = .{ .ty_pl = .{ .ty = try sema.addType(ptr_field_ty), .payload = try sema.addExtra(Air.StructField{ - .struct_ptr = struct_ptr, + .struct_operand = struct_ptr, .field_index = @intCast(u32, field_index), }), } }, }); } -fn analyzeUnionFieldPtr( +fn structFieldVal( + sema: *Sema, + block: *Scope.Block, + src: LazySrcLoc, + struct_byval: Air.Inst.Ref, + field_name: []const u8, + field_name_src: LazySrcLoc, + unresolved_struct_ty: Type, +) CompileError!Air.Inst.Ref { + assert(unresolved_struct_ty.zigTypeTag() == .Struct); + + const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty); + const struct_obj = struct_ty.castTag(.@"struct").?.data; + + const field_index = struct_obj.fields.getIndex(field_name) orelse + return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name); + const field = struct_obj.fields.values()[field_index]; + + if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| { + if (struct_val.isUndef()) return sema.addConstUndef(field.ty); + + const field_values = struct_val.castTag(.@"struct").?.data; + return sema.addConstant(field.ty, field_values[field_index]); + } + + try sema.requireRuntimeBlock(block, src); + return block.addInst(.{ + .tag = .struct_field_val, + .data = .{ .ty_pl = .{ + .ty = try sema.addType(field.ty), + .payload = try sema.addExtra(Air.StructField{ + .struct_operand = struct_byval, + .field_index = @intCast(u32, field_index), + }), + } }, + }); +} + +fn unionFieldPtr( sema: *Sema, block: *Scope.Block, src: LazySrcLoc, @@ -6847,6 +7066,37 @@ fn analyzeUnionFieldPtr( return mod.fail(&block.base, src, "TODO implement runtime union field access", .{}); } +fn unionFieldVal( + sema: *Sema, + block: *Scope.Block, + src: LazySrcLoc, + union_byval: Air.Inst.Ref, + field_name: []const u8, + field_name_src: LazySrcLoc, + unresolved_union_ty: Type, +) CompileError!Air.Inst.Ref { + assert(unresolved_union_ty.zigTypeTag() == .Union); + + const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty); + const union_obj = union_ty.cast(Type.Payload.Union).?.data; + + const field_index = union_obj.fields.getIndex(field_name) orelse + return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name); + + const field = union_obj.fields.values()[field_index]; + + if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| { + if (union_val.isUndef()) return sema.addConstUndef(field.ty); + + // TODO detect inactive union field and emit compile error + const active_val = union_val.castTag(.@"union").?.data.val; + return sema.addConstant(field.ty, active_val); + } + + try sema.requireRuntimeBlock(block, src); + return sema.mod.fail(&block.base, src, "TODO implement runtime union field access", .{}); +} + fn elemPtr( sema: *Sema, block: *Scope.Block, @@ -6973,7 +7223,7 @@ fn coerce( const arena = sema.arena; // undefined to anything - if (try sema.resolvePossiblyUndefinedValue(block, inst_src, inst)) |val| { + if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| { if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) { return sema.addConstant(dest_type, val); } @@ -7207,8 +7457,8 @@ fn storePtr( if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null) return; - if (try sema.resolvePossiblyUndefinedValue(block, src, ptr)) |ptr_val| blk: { - const const_val = (try sema.resolvePossiblyUndefinedValue(block, src, value)) orelse + if (try sema.resolveMaybeUndefVal(block, src, ptr)) |ptr_val| blk: { + const const_val = (try sema.resolveMaybeUndefVal(block, src, value)) orelse return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{}); if (ptr_val.tag() == .int_u64) @@ -7252,7 +7502,7 @@ fn bitcast( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { - if (try sema.resolvePossiblyUndefinedValue(block, inst_src, inst)) |val| { + if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| { // Keep the comptime Value representation; take the new type. return sema.addConstant(dest_type, val); } @@ -7358,7 +7608,7 @@ fn analyzeRef( const operand_ty = sema.typeOf(operand); const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One); - if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |val| { + if (try sema.resolveMaybeUndefVal(block, src, operand)) |val| { return sema.addConstant(ptr_type, try Value.Tag.ref_val.create(sema.arena, val)); } @@ -7395,7 +7645,7 @@ fn analyzeSliceLen( src: LazySrcLoc, slice_inst: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { - if (try sema.resolvePossiblyUndefinedValue(block, src, slice_inst)) |slice_val| { + if (try sema.resolveMaybeUndefVal(block, src, slice_inst)) |slice_val| { if (slice_val.isUndef()) { return sema.addConstUndef(Type.initTag(.usize)); } @@ -7413,7 +7663,7 @@ fn analyzeIsNull( invert_logic: bool, ) CompileError!Air.Inst.Ref { const result_ty = Type.initTag(.bool); - if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |opt_val| { + if (try sema.resolveMaybeUndefVal(block, src, operand)) |opt_val| { if (opt_val.isUndef()) { return sema.addConstUndef(result_ty); } @@ -7442,7 +7692,7 @@ fn analyzeIsNonErr( if (ot == .ErrorSet) return Air.Inst.Ref.bool_false; assert(ot == .ErrorUnion); const result_ty = Type.initTag(.bool); - if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| { + if (try sema.resolveMaybeUndefVal(block, src, operand)) |err_union| { if (err_union.isUndef()) { return sema.addConstUndef(result_ty); } @@ -7567,8 +7817,8 @@ fn cmpNumeric( }); } - if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, lhs)) |lhs_val| { - if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, rhs)) |rhs_val| { + if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { if (lhs_val.isUndef() or rhs_val.isUndef()) { return sema.addConstUndef(Type.initTag(.bool)); } @@ -7635,7 +7885,7 @@ fn cmpNumeric( var dest_float_type: ?Type = null; var lhs_bits: usize = undefined; - if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { if (lhs_val.isUndef()) return sema.addConstUndef(Type.initTag(.bool)); const is_unsigned = if (lhs_is_float) x: { @@ -7670,7 +7920,7 @@ fn cmpNumeric( } var rhs_bits: usize = undefined; - if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, rhs)) |rhs_val| { + if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { if (rhs_val.isUndef()) return sema.addConstUndef(Type.initTag(.bool)); const is_unsigned = if (rhs_is_float) x: { @@ -7725,7 +7975,7 @@ fn wrapOptional( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { - if (try sema.resolvePossiblyUndefinedValue(block, inst_src, inst)) |val| { + if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| { return sema.addConstant(dest_type, val); } @@ -7743,7 +7993,7 @@ fn wrapErrorUnion( const inst_ty = sema.typeOf(inst); const dest_err_set_ty = dest_type.errorUnionSet(); const dest_payload_ty = dest_type.errorUnionPayload(); - if (try sema.resolvePossiblyUndefinedValue(block, inst_src, inst)) |val| { + if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| { if (inst_ty.zigTypeTag() != .ErrorSet) { _ = try sema.coerce(block, dest_payload_ty, inst, inst_src); } else switch (dest_err_set_ty.tag()) { @@ -7956,7 +8206,7 @@ fn getBuiltin( const mod = sema.mod; const std_pkg = mod.main_pkg.table.get("std").?; const std_file = (mod.importPkg(std_pkg) catch unreachable).file; - const opt_builtin_inst = try sema.analyzeNamespaceLookup( + const opt_builtin_inst = try sema.namespaceLookupRef( block, src, std_file.root_decl.?.namespace, @@ -7964,7 +8214,7 @@ fn getBuiltin( ); const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src); const builtin_ty = try sema.analyzeAsType(block, src, builtin_inst); - const opt_ty_inst = try sema.analyzeNamespaceLookup( + const opt_ty_inst = try sema.namespaceLookupRef( block, src, builtin_ty.getNamespace().?, @@ -8320,5 +8570,5 @@ fn isComptimeKnown( src: LazySrcLoc, inst: Air.Inst.Ref, ) !bool { - return (try sema.resolvePossiblyUndefinedValue(block, src, inst)) != null; + return (try sema.resolveMaybeUndefVal(block, src, inst)) != null; } diff --git a/src/codegen.zig b/src/codegen.zig index c2504f26a8..4924b68ca3 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -851,6 +851,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .ret => try self.airRet(inst), .store => try self.airStore(inst), .struct_field_ptr=> try self.airStructFieldPtr(inst), + .struct_field_val=> try self.airStructFieldVal(inst), .switch_br => try self.airSwitch(inst), .varptr => try self.airVarPtr(inst), .slice_ptr => try self.airSlicePtr(inst), @@ -1501,6 +1502,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); } + fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; + _ = extra; + return self.fail("TODO implement codegen struct_field_val", .{}); + //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); + } + fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool { return switch (mcv) { .none => unreachable, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 705c5c2ad1..fa254af293 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -935,6 +935,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM .wrap_optional => try airWrapOptional(o, inst), .ref => try airRef(o, inst), .struct_field_ptr => try airStructFieldPtr(o, inst), + .struct_field_val => try airStructFieldVal(o, inst), .varptr => try airVarPtr(o, inst), .slice_ptr => try airSliceField(o, inst, ".ptr;\n"), .slice_len => try airSliceField(o, inst, ".len;\n"), @@ -1660,8 +1661,8 @@ fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue { const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; const extra = o.air.extraData(Air.StructField, ty_pl.payload).data; const writer = o.writer(); - const struct_ptr = try o.resolveInst(extra.struct_ptr); - const struct_ptr_ty = o.air.typeOf(extra.struct_ptr); + const struct_ptr = try o.resolveInst(extra.struct_operand); + const struct_ptr_ty = o.air.typeOf(extra.struct_operand); const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data; const field_name = struct_obj.fields.keys()[extra.field_index]; @@ -1680,6 +1681,26 @@ fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue { return local; } +fn airStructFieldVal(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; + const extra = o.air.extraData(Air.StructField, ty_pl.payload).data; + const writer = o.writer(); + const struct_byval = try o.resolveInst(extra.struct_operand); + const struct_ty = o.air.typeOf(extra.struct_operand); + const struct_obj = struct_ty.castTag(.@"struct").?.data; + const field_name = struct_obj.fields.keys()[extra.field_index]; + + const inst_ty = o.air.typeOfIndex(inst); + const local = try o.allocLocal(inst_ty, .Const); + try writer.writeAll(" = "); + try o.writeCValue(writer, struct_byval); + try writer.print(".{};\n", .{fmtIdent(field_name)}); + return local; +} + // *(E!T) -> E NOT *E fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue { if (o.liveness.isUnused(inst)) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 4224591b0b..1b3c36fc69 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -350,19 +350,21 @@ pub const Object = struct { air: Air, liveness: Liveness, ) !void { + const decl = func.owner_decl; + var dg: DeclGen = .{ .context = self.context, .object = self, .module = module, - .decl = func.owner_decl, + .decl = decl, .err_msg = null, .gpa = module.gpa, }; - const llvm_func = try dg.resolveLLVMFunction(func.owner_decl); + const llvm_func = try dg.resolveLlvmFunction(decl); // This gets the LLVM values from the function and stores them in `dg.args`. - const fn_param_len = func.owner_decl.ty.fnParamLen(); + const fn_param_len = decl.ty.fnParamLen(); var args = try dg.gpa.alloc(*const llvm.Value, fn_param_len); for (args) |*arg, i| { @@ -400,13 +402,16 @@ pub const Object = struct { fg.genBody(air.getMainBody()) catch |err| switch (err) { error.CodegenFail => { - func.owner_decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, func.owner_decl, dg.err_msg.?); + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, dg.err_msg.?); dg.err_msg = null; return; }, 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 updateDecl(self: *Object, module: *Module, decl: *Module.Decl) !void { @@ -428,6 +433,38 @@ pub const Object = struct { else => |e| return e, }; } + + pub fn updateDeclExports( + self: *Object, + module: *const Module, + decl: *const Module.Decl, + exports: []const *Module.Export, + ) !void { + const llvm_fn = self.llvm_module.getNamedFunction(decl.name).?; + const is_extern = decl.val.tag() == .extern_fn; + if (is_extern or exports.len != 0) { + llvm_fn.setLinkage(.External); + llvm_fn.setUnnamedAddr(.False); + } else { + llvm_fn.setLinkage(.Internal); + llvm_fn.setUnnamedAddr(.True); + } + // TODO LLVM C API does not support deleting aliases. We need to + // patch it to support this or figure out how to wrap the C++ API ourselves. + // Until then we iterate over existing aliases and make them point + // to the correct decl, or otherwise add a new alias. Old aliases are leaked. + for (exports) |exp| { + if (self.llvm_module.getNamedGlobalAlias(exp.options.name.ptr, exp.options.name.len)) |alias| { + alias.setAliasee(llvm_fn); + } else { + const exp_name_z = try module.gpa.dupeZ(u8, exp.options.name); + defer module.gpa.free(exp_name_z); + + const alias = self.llvm_module.addAlias(llvm_fn.typeOf(), llvm_fn, exp_name_z); + _ = alias; + } + } + } }; pub const DeclGen = struct { @@ -461,21 +498,19 @@ pub const DeclGen = struct { _ = func_payload; @panic("TODO llvm backend genDecl function pointer"); } else if (decl.val.castTag(.extern_fn)) |extern_fn| { - _ = try self.resolveLLVMFunction(extern_fn.data); + _ = try self.resolveLlvmFunction(extern_fn.data); } else { _ = try self.resolveGlobalDecl(decl); } } /// If the llvm function does not exist, create it - fn resolveLLVMFunction(self: *DeclGen, func: *Module.Decl) !*const llvm.Value { - // TODO: do we want to store this in our own datastructure? - if (self.llvmModule().getNamedFunction(func.name)) |llvm_fn| return llvm_fn; + fn resolveLlvmFunction(self: *DeclGen, decl: *Module.Decl) !*const llvm.Value { + if (self.llvmModule().getNamedFunction(decl.name)) |llvm_fn| return llvm_fn; - assert(func.has_tv); - const zig_fn_type = func.ty; + assert(decl.has_tv); + const zig_fn_type = decl.ty; const return_type = zig_fn_type.fnReturnType(); - const fn_param_len = zig_fn_type.fnParamLen(); const fn_param_types = try self.gpa.alloc(Type, fn_param_len); @@ -495,9 +530,17 @@ pub const DeclGen = struct { @intCast(c_uint, fn_param_len), .False, ); - const llvm_fn = self.llvmModule().addFunction(func.name, fn_type); + const llvm_fn = self.llvmModule().addFunction(decl.name, fn_type); - if (return_type.tag() == .noreturn) { + const is_extern = decl.val.tag() == .extern_fn; + if (!is_extern) { + llvm_fn.setLinkage(.Internal); + llvm_fn.setUnnamedAddr(.True); + } + + // TODO: calling convention, linkage, tsan, etc. see codegen.cpp `make_fn_llvm_value`. + + if (return_type.isNoReturn()) { self.addFnAttr(llvm_fn, "noreturn"); } @@ -505,7 +548,6 @@ pub const DeclGen = struct { } fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value { - // TODO: do we want to store this in our own datastructure? if (self.llvmModule().getNamedGlobal(decl.name)) |val| return val; assert(decl.has_tv); @@ -515,9 +557,11 @@ pub const DeclGen = struct { const global = self.llvmModule().addGlobal(llvm_type, decl.name); const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { const variable = payload.data; - global.setGlobalConstant(.False); break :init_val variable.init; - } else decl.val; + } 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); @@ -602,12 +646,13 @@ pub const DeclGen = struct { llvm_param.* = try self.llvmType(t.fnParamType(i)); } const is_var_args = t.fnIsVarArgs(); - return llvm.functionType( + const llvm_fn_ty = llvm.functionType( ret_ty, llvm_params.ptr, @intCast(c_uint, llvm_params.len), llvm.Bool.fromBool(is_var_args), ); + return llvm_fn_ty.pointerType(0); }, .ComptimeInt => unreachable, .ComptimeFloat => unreachable, @@ -717,6 +762,42 @@ pub const DeclGen = struct { 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; + + return self.resolveLlvmFunction(fn_decl); + }, + .ErrorSet => { + const llvm_ty = try self.llvmType(tv.ty); + switch (tv.val.tag()) { + .@"error" => { + const err_name = tv.val.castTag(.@"error").?.data.name; + const kv = try self.module.getErrorValue(err_name); + return llvm_ty.constInt(kv.value, .False); + }, + else => { + // In this case we are rendering an error union which has a 0 bits payload. + return llvm_ty.constNull(); + }, + } + }, + .ErrorUnion => { + const error_type = tv.ty.errorUnionSet(); + const payload_type = tv.ty.errorUnionPayload(); + const sub_val = tv.val.castTag(.error_union).?.data; + + 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.todo("implement error union const of type '{}'", .{tv.ty}); + }, else => return self.todo("implement const of type '{}'", .{tv.ty}), } } @@ -801,8 +882,17 @@ pub const FuncGen = struct { for (body) |inst| { const opt_value: ?*const llvm.Value = switch (air_tags[inst]) { // zig fmt: off - .add => try self.airAdd(inst), - .sub => try self.airSub(inst), + .add => try self.airAdd(inst, false), + .addwrap => try self.airAdd(inst, true), + .sub => try self.airSub(inst, false), + .subwrap => try self.airSub(inst, true), + .mul => try self.airMul(inst, false), + .mulwrap => try self.airMul(inst, true), + .div => try self.airDiv(inst), + + .bit_and, .bool_and => try self.airAnd(inst), + .bit_or, .bool_or => try self.airOr(inst), + .xor => try self.airXor(inst), .cmp_eq => try self.airCmp(inst, .eq), .cmp_gt => try self.airCmp(inst, .gt), @@ -825,10 +915,12 @@ pub const FuncGen = struct { .bitcast => try self.airBitCast(inst), .block => try self.airBlock(inst), .br => try self.airBr(inst), + .switch_br => try self.airSwitchBr(inst), .breakpoint => try self.airBreakpoint(inst), .call => try self.airCall(inst), .cond_br => try self.airCondBr(inst), .intcast => try self.airIntCast(inst), + .floatcast => try self.airFloatCast(inst), .ptrtoint => try self.airPtrToInt(inst), .load => try self.airLoad(inst), .loop => try self.airLoop(inst), @@ -840,6 +932,9 @@ pub const FuncGen = struct { .slice_ptr => try self.airSliceField(inst, 0), .slice_len => try self.airSliceField(inst, 1), + .struct_field_ptr => try self.airStructFieldPtr(inst), + .struct_field_val => try self.airStructFieldVal(inst), + .slice_elem_val => try self.airSliceElemVal(inst, false), .ptr_slice_elem_val => try self.airSliceElemVal(inst, true), @@ -851,12 +946,18 @@ pub const FuncGen = struct { .unwrap_errunion_err => try self.airErrUnionErr(inst, false), .unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true), + .wrap_optional => try self.airWrapOptional(inst), + .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), + .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + + .constant => unreachable, + .const_ty => unreachable, + .ref => unreachable, // TODO eradicate this instruction .unreach => self.airUnreach(inst), .dbg_stmt => blk: { // TODO: implement debug info break :blk null; }, - else => |tag| return self.todo("implement AIR instruction: {}", .{tag}), // zig fmt: on }; if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val); @@ -867,47 +968,32 @@ pub const FuncGen = struct { const pl_op = self.air.instructions.items(.data)[inst].pl_op; const extra = self.air.extraData(Air.Call, pl_op.payload); const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const zig_fn_type = self.air.typeOf(pl_op.operand); + const return_type = zig_fn_type.fnReturnType(); + const llvm_fn = try self.resolveInst(pl_op.operand); - if (self.air.value(pl_op.operand)) |func_value| { - const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| - extern_fn.data - else if (func_value.castTag(.function)) |func_payload| - func_payload.data.owner_decl - else - unreachable; + const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, args.len); + defer self.gpa.free(llvm_param_vals); - assert(fn_decl.has_tv); - const zig_fn_type = fn_decl.ty; - const llvm_fn = try self.dg.resolveLLVMFunction(fn_decl); - - const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, args.len); - defer self.gpa.free(llvm_param_vals); - - for (args) |arg, i| { - llvm_param_vals[i] = try self.resolveInst(arg); - } - - // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs - // Do we need that? - const call = self.builder.buildCall( - llvm_fn, - llvm_param_vals.ptr, - @intCast(c_uint, args.len), - "", - ); - - const return_type = zig_fn_type.fnReturnType(); - if (return_type.tag() == .noreturn) { - _ = self.builder.buildUnreachable(); - } - - // No need to store the LLVM value if the return type is void or noreturn - if (!return_type.hasCodeGenBits()) return null; - - return call; - } else { - return self.todo("implement calling runtime known function pointer", .{}); + for (args) |arg, i| { + llvm_param_vals[i] = try self.resolveInst(arg); } + + const call = self.builder.buildCall( + llvm_fn, + llvm_param_vals.ptr, + @intCast(c_uint, args.len), + "", + ); + + if (return_type.isNoReturn()) { + _ = self.builder.buildUnreachable(); + } + + // No need to store the LLVM value if the return type is void or noreturn + if (!return_type.hasCodeGenBits()) return null; + + return call; } fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -1026,6 +1112,11 @@ pub const FuncGen = struct { return null; } + fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + _ = inst; + return self.todo("implement llvm codegen for switch_br", .{}); + } + fn airLoop(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const loop = self.air.extraData(Air.Block, ty_pl.payload); @@ -1082,10 +1173,32 @@ pub const FuncGen = struct { }; const indices: [1]*const llvm.Value = .{rhs}; - const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, 1, ""); + const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); return self.builder.buildLoad(ptr, ""); } + fn airStructFieldPtr(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 struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; + const struct_ptr = try self.resolveInst(struct_field.struct_operand); + const field_index = @intCast(c_uint, struct_field.field_index); + return self.builder.buildStructGEP(struct_ptr, field_index, ""); + } + + fn airStructFieldVal(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 struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; + const struct_byval = try self.resolveInst(struct_field.struct_operand); + const field_index = @intCast(c_uint, struct_field.field_index); + return self.builder.buildExtractValue(struct_byval, field_index, ""); + } + fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; @@ -1321,7 +1434,7 @@ pub const FuncGen = struct { _ = operand; _ = operand_is_ptr; - return self.todo("implement 'airErrUnionPayload' for type {}", .{self.air.typeOf(ty_op.operand)}); + return self.todo("implement llvm codegen for 'airErrUnionPayload' for type {}", .{self.air.typeOf(ty_op.operand)}); } fn airErrUnionErr( @@ -1332,42 +1445,123 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; - _ = operand_is_ptr; - return self.todo("implement 'airErrUnionErr'", .{}); + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const operand = try self.resolveInst(ty_op.operand); + const operand_ty = self.air.typeOf(ty_op.operand); + + const payload_ty = operand_ty.errorUnionPayload(); + if (!payload_ty.hasCodeGenBits()) { + if (!operand_is_ptr) return operand; + return self.builder.buildLoad(operand, ""); + } + return self.todo("implement llvm codegen for 'airErrUnionErr'", .{}); } - fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; + + return self.todo("implement llvm codegen for 'airWrapOptional'", .{}); + } + + fn airWrapErrUnionPayload(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + + return self.todo("implement llvm codegen for 'airWrapErrUnionPayload'", .{}); + } + + fn airWrapErrUnionErr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + + return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{}); + } + + fn airAdd(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); - if (!inst_ty.isInt()) - return self.todo("implement 'airAdd' for type {}", .{inst_ty}); - - return if (inst_ty.isSignedInt()) - self.builder.buildNSWAdd(lhs, rhs, "") - else - self.builder.buildNUWAdd(lhs, rhs, ""); + if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, ""); + if (wrap) return self.builder.buildAdd(lhs, rhs, ""); + if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); + return self.builder.buildNUWAdd(lhs, rhs, ""); } - fn airSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + fn airSub(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); - if (!inst_ty.isInt()) - return self.todo("implement 'airSub' for type {}", .{inst_ty}); + if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, ""); + if (wrap) return self.builder.buildSub(lhs, rhs, ""); + if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); + return self.builder.buildNUWSub(lhs, rhs, ""); + } - return if (inst_ty.isSignedInt()) - self.builder.buildNSWSub(lhs, rhs, "") - else - self.builder.buildNUWSub(lhs, rhs, ""); + fn airMul(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const inst_ty = self.air.typeOfIndex(inst); + + if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, ""); + if (wrap) return self.builder.buildMul(lhs, rhs, ""); + if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); + return self.builder.buildNUWMul(lhs, rhs, ""); + } + + fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const inst_ty = self.air.typeOfIndex(inst); + + if (inst_ty.isFloat()) return self.builder.buildFDiv(lhs, rhs, ""); + if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); + return self.builder.buildUDiv(lhs, rhs, ""); + } + + fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + return self.builder.buildAnd(lhs, rhs, ""); + } + + fn airOr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + return self.builder.buildOr(lhs, rhs, ""); + } + + fn airXor(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + return self.builder.buildXor(lhs, rhs, ""); } fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -1384,6 +1578,14 @@ pub const FuncGen = struct { return self.builder.buildIntCast2(operand, try self.dg.llvmType(inst_ty), llvm.Bool.fromBool(signed), ""); } + fn airFloatCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + + // TODO split floatcast AIR into float_widen and float_shorten + return self.todo("implement 'airFloatCast'", .{}); + } + fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; @@ -1474,8 +1676,8 @@ pub const FuncGen = struct { fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { _ = inst; - const llvn_fn = self.getIntrinsic("llvm.debugtrap"); - _ = self.builder.buildCall(llvn_fn, undefined, 0, ""); + const llvm_fn = self.getIntrinsic("llvm.debugtrap"); + _ = self.builder.buildCall(llvm_fn, undefined, 0, ""); return null; } diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 3b76361030..9ef6d7e1ac 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -82,6 +82,24 @@ pub const Value = opaque { pub const setGlobalConstant = LLVMSetGlobalConstant; extern fn LLVMSetGlobalConstant(GlobalVar: *const Value, IsConstant: Bool) void; + + pub const setLinkage = LLVMSetLinkage; + extern fn LLVMSetLinkage(Global: *const Value, Linkage: Linkage) void; + + pub const setUnnamedAddr = LLVMSetUnnamedAddr; + extern fn LLVMSetUnnamedAddr(Global: *const Value, HasUnnamedAddr: Bool) void; + + pub const deleteGlobal = LLVMDeleteGlobal; + extern fn LLVMDeleteGlobal(GlobalVar: *const Value) void; + + pub const getNextGlobalAlias = LLVMGetNextGlobalAlias; + extern fn LLVMGetNextGlobalAlias(GA: *const Value) *const Value; + + pub const getAliasee = LLVMAliasGetAliasee; + extern fn LLVMAliasGetAliasee(Alias: *const Value) *const Value; + + pub const setAliasee = LLVMAliasSetAliasee; + extern fn LLVMAliasSetAliasee(Alias: *const Value, Aliasee: *const Value) void; }; pub const Type = opaque { @@ -145,6 +163,27 @@ pub const Module = opaque { pub const dump = LLVMDumpModule; extern fn LLVMDumpModule(M: *const Module) void; + + pub const getFirstGlobalAlias = LLVMGetFirstGlobalAlias; + extern fn LLVMGetFirstGlobalAlias(M: *const Module) *const Value; + + pub const getLastGlobalAlias = LLVMGetLastGlobalAlias; + extern fn LLVMGetLastGlobalAlias(M: *const Module) *const Value; + + pub const addAlias = LLVMAddAlias; + extern fn LLVMAddAlias( + M: *const Module, + Ty: *const Type, + Aliasee: *const Value, + Name: [*:0]const u8, + ) *const Value; + + pub const getNamedGlobalAlias = LLVMGetNamedGlobalAlias; + extern fn LLVMGetNamedGlobalAlias( + M: *const Module, + Name: [*]const u8, + NameLen: usize, + ) ?*const Value; }; pub const lookupIntrinsicID = LLVMLookupIntrinsicID; @@ -252,18 +291,60 @@ pub const Builder = opaque { pub const buildNot = LLVMBuildNot; extern fn LLVMBuildNot(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value; + pub const buildFAdd = LLVMBuildFAdd; + extern fn LLVMBuildFAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildAdd = LLVMBuildAdd; + extern fn LLVMBuildAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildNSWAdd = LLVMBuildNSWAdd; extern fn LLVMBuildNSWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; pub const buildNUWAdd = LLVMBuildNUWAdd; extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildFSub = LLVMBuildFSub; + extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildSub = LLVMBuildSub; + extern fn LLVMBuildSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildNSWSub = LLVMBuildNSWSub; extern fn LLVMBuildNSWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; pub const buildNUWSub = LLVMBuildNUWSub; extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildFMul = LLVMBuildFMul; + extern fn LLVMBuildFMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildMul = LLVMBuildMul; + extern fn LLVMBuildMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildNSWMul = LLVMBuildNSWMul; + extern fn LLVMBuildNSWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildNUWMul = LLVMBuildNUWMul; + extern fn LLVMBuildNUWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildUDiv = LLVMBuildUDiv; + extern fn LLVMBuildUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildSDiv = LLVMBuildSDiv; + extern fn LLVMBuildSDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildFDiv = LLVMBuildFDiv; + extern fn LLVMBuildFDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildAnd = LLVMBuildAnd; + extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildOr = LLVMBuildOr; + extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildXor = LLVMBuildXor; + extern fn LLVMBuildXor(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildIntCast2 = LLVMBuildIntCast2; extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: Bool, Name: [*:0]const u8) *const Value; @@ -279,6 +360,16 @@ pub const Builder = opaque { Name: [*:0]const u8, ) *const Value; + pub const buildInBoundsGEP2 = LLVMBuildInBoundsGEP2; + extern fn LLVMBuildInBoundsGEP2( + B: *const Builder, + Ty: *const Type, + Pointer: *const Value, + Indices: [*]const *const Value, + NumIndices: c_uint, + Name: [*:0]const u8, + ) *const Value; + pub const buildICmp = LLVMBuildICmp; extern fn LLVMBuildICmp(*const Builder, Op: IntPredicate, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; @@ -292,10 +383,28 @@ pub const Builder = opaque { extern fn LLVMBuildPhi(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value; pub const buildExtractValue = LLVMBuildExtractValue; - extern fn LLVMBuildExtractValue(*const Builder, AggVal: *const Value, Index: c_uint, Name: [*:0]const u8) *const Value; + extern fn LLVMBuildExtractValue( + *const Builder, + AggVal: *const Value, + Index: c_uint, + Name: [*:0]const u8, + ) *const Value; pub const buildPtrToInt = LLVMBuildPtrToInt; - extern fn LLVMBuildPtrToInt(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value; + extern fn LLVMBuildPtrToInt( + *const Builder, + Val: *const Value, + DestTy: *const Type, + Name: [*:0]const u8, + ) *const Value; + + pub const buildStructGEP = LLVMBuildStructGEP; + extern fn LLVMBuildStructGEP( + B: *const Builder, + Pointer: *const Value, + Idx: c_uint, + Name: [*:0]const u8, + ) *const Value; }; pub const IntPredicate = enum(c_int) { @@ -715,3 +824,23 @@ extern fn ZigLLVMWriteImportLibrary( output_lib_path: [*c]const u8, kill_at: bool, ) bool; + +pub const Linkage = enum(c_uint) { + External, + AvailableExternally, + LinkOnceAny, + LinkOnceODR, + LinkOnceODRAutoHide, + WeakAny, + WeakODR, + Appending, + Internal, + Private, + DLLImport, + DLLExport, + ExternalWeak, + Ghost, + Common, + LinkerPrivate, + LinkerPrivateWeak, +}; diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index ca0d53988d..bf9010fbff 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -1306,7 +1306,7 @@ pub const Context = struct { fn airStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.StructField, ty_pl.payload); - const struct_ptr = self.resolveInst(extra.data.struct_ptr); + const struct_ptr = self.resolveInst(extra.data.struct_operand); return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, extra.data.field_index) }; } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 0bae2cc6cc..0c9e513742 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -777,8 +777,18 @@ pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {}; } -pub fn updateDeclExports(self: *Coff, module: *Module, decl: *Module.Decl, exports: []const *Module.Export) !void { - if (self.llvm_object) |_| return; +pub fn updateDeclExports( + self: *Coff, + module: *Module, + decl: *Module.Decl, + exports: []const *Module.Export, +) !void { + if (build_options.skip_non_native and builtin.object_format != .coff) { + @panic("Attempted to compile for object format that was disabled by build configuration"); + } + if (build_options.have_llvm) { + if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); + } for (exports) |exp| { if (exp.options.section) |section_name| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index c95af23026..502575f3c8 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2716,7 +2716,12 @@ pub fn updateDeclExports( decl: *Module.Decl, exports: []const *Module.Export, ) !void { - if (self.llvm_object) |_| return; + if (build_options.skip_non_native and builtin.object_format != .elf) { + @panic("Attempted to compile for object format that was disabled by build configuration"); + } + if (build_options.have_llvm) { + if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); + } const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 21f4e9c33c..1f2e0616ba 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3785,6 +3785,12 @@ pub fn updateDeclExports( decl: *Module.Decl, exports: []const *Module.Export, ) !void { + if (build_options.skip_non_native and builtin.object_format != .macho) { + @panic("Attempted to compile for object format that was disabled by build configuration"); + } + if (build_options.have_llvm) { + if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); + } const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 2ed6576033..23d0543494 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -330,10 +330,12 @@ pub fn updateDeclExports( decl: *const Module.Decl, exports: []const *Module.Export, ) !void { - _ = self; - _ = module; - _ = decl; - _ = exports; + if (build_options.skip_non_native and builtin.object_format != .wasm) { + @panic("Attempted to compile for object format that was disabled by build configuration"); + } + if (build_options.have_llvm) { + if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); + } } pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { diff --git a/src/print_air.zig b/src/print_air.zig index 2c756670ad..c20a6995e5 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -171,7 +171,8 @@ const Writer = struct { .loop, => try w.writeBlock(s, inst), - .struct_field_ptr => try w.writeStructFieldPtr(s, inst), + .struct_field_ptr => try w.writeStructField(s, inst), + .struct_field_val => try w.writeStructField(s, inst), .varptr => try w.writeVarPtr(s, inst), .constant => try w.writeConstant(s, inst), .assembly => try w.writeAssembly(s, inst), @@ -233,11 +234,11 @@ const Writer = struct { try s.writeAll("}"); } - fn writeStructFieldPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; const extra = w.air.extraData(Air.StructField, ty_pl.payload); - try w.writeOperand(s, inst, 0, extra.data.struct_ptr); + try w.writeOperand(s, inst, 0, extra.data.struct_operand); try s.print(", {d}", .{extra.data.field_index}); }