diff options
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/c.zig | 220 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 223 | ||||
| -rw-r--r-- | src/codegen/spirv.zig | 132 |
3 files changed, 242 insertions, 333 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 818267a8b8..1725658a37 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -646,8 +646,7 @@ pub const DeclGen = struct { fn renderAnonDeclValue( dg: *DeclGen, writer: anytype, - ptr_val: Value, - anon_decl: InternPool.Key.Ptr.Addr.AnonDecl, + anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl, location: ValueRenderLocation, ) error{ OutOfMemory, AnalysisFail }!void { const zcu = dg.zcu; @@ -657,16 +656,16 @@ pub const DeclGen = struct { const decl_ty = decl_val.typeOf(zcu); // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. - const ptr_ty = ptr_val.typeOf(zcu); + const ptr_ty = Type.fromInterned(anon_decl.orig_ty); if (ptr_ty.isPtrAtRuntime(zcu) and !decl_ty.isFnOrHasRuntimeBits(zcu)) { return dg.writeCValue(writer, .{ .undef = ptr_ty }); } // Chase function values in order to be able to reference the original function. if (decl_val.getFunction(zcu)) |func| - return dg.renderDeclValue(writer, ptr_val, func.owner_decl, location); + return dg.renderDeclValue(writer, func.owner_decl, location); if (decl_val.getExternFunc(zcu)) |extern_func| - return dg.renderDeclValue(writer, ptr_val, extern_func.decl, location); + return dg.renderDeclValue(writer, extern_func.decl, location); assert(decl_val.getVariable(zcu) == null); @@ -712,7 +711,6 @@ pub const DeclGen = struct { fn renderDeclValue( dg: *DeclGen, writer: anytype, - val: Value, decl_index: InternPool.DeclIndex, location: ValueRenderLocation, ) error{ OutOfMemory, AnalysisFail }!void { @@ -722,17 +720,17 @@ pub const DeclGen = struct { assert(decl.has_tv); // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. - const ty = val.typeOf(zcu); const decl_ty = decl.typeOf(zcu); - if (ty.isPtrAtRuntime(zcu) and !decl_ty.isFnOrHasRuntimeBits(zcu)) { - return dg.writeCValue(writer, .{ .undef = ty }); + const ptr_ty = try decl.declPtrType(zcu); + if (!decl_ty.isFnOrHasRuntimeBits(zcu)) { + return dg.writeCValue(writer, .{ .undef = ptr_ty }); } // Chase function values in order to be able to reference the original function. if (decl.val.getFunction(zcu)) |func| if (func.owner_decl != decl_index) - return dg.renderDeclValue(writer, val, func.owner_decl, location); + return dg.renderDeclValue(writer, func.owner_decl, location); if (decl.val.getExternFunc(zcu)) |extern_func| if (extern_func.decl != decl_index) - return dg.renderDeclValue(writer, val, extern_func.decl, location); + return dg.renderDeclValue(writer, extern_func.decl, location); if (decl.val.getVariable(zcu)) |variable| try dg.renderFwdDecl(decl_index, variable, .tentative); @@ -740,7 +738,7 @@ pub const DeclGen = struct { // them). The analysis until now should ensure that the C function // pointers are compatible. If they are not, then there is a bug // somewhere and we should let the C compiler tell us about it. - const ctype = try dg.ctypeFromType(ty, .complete); + const ctype = try dg.ctypeFromType(ptr_ty, .complete); const elem_ctype = ctype.info(ctype_pool).pointer.elem_ctype; const decl_ctype = try dg.ctypeFromType(decl_ty, .complete); const need_cast = !elem_ctype.eql(decl_ctype) and @@ -755,125 +753,108 @@ pub const DeclGen = struct { if (need_cast) try writer.writeByte(')'); } - /// Renders a "parent" pointer by recursing to the root decl/variable - /// that its contents are defined with respect to. - fn renderParentPtr( + fn renderPointer( dg: *DeclGen, writer: anytype, - ptr_val: InternPool.Index, + derivation: Value.PointerDeriveStep, location: ValueRenderLocation, ) error{ OutOfMemory, AnalysisFail }!void { const zcu = dg.zcu; - const ip = &zcu.intern_pool; - const ptr_ty = Type.fromInterned(ip.typeOf(ptr_val)); - const ptr_ctype = try dg.ctypeFromType(ptr_ty, .complete); - const ptr_child_ctype = ptr_ctype.info(&dg.ctype_pool).pointer.elem_ctype; - const ptr = ip.indexToKey(ptr_val).ptr; - switch (ptr.addr) { - .decl => |d| try dg.renderDeclValue(writer, Value.fromInterned(ptr_val), d, location), - .anon_decl => |anon_decl| try dg.renderAnonDeclValue(writer, Value.fromInterned(ptr_val), anon_decl, location), + switch (derivation) { + .comptime_alloc_ptr, .comptime_field_ptr => unreachable, .int => |int| { + const ptr_ctype = try dg.ctypeFromType(int.ptr_ty, .complete); + const addr_val = try zcu.intValue(Type.usize, int.addr); try writer.writeByte('('); try dg.renderCType(writer, ptr_ctype); - try writer.print("){x}", .{try dg.fmtIntLiteral(Value.fromInterned(int), .Other)}); + try writer.print("){x}", .{try dg.fmtIntLiteral(addr_val, .Other)}); }, - .eu_payload, .opt_payload => |base| { - const ptr_base_ty = Type.fromInterned(ip.typeOf(base)); - const base_ty = ptr_base_ty.childType(zcu); - // Ensure complete type definition is visible before accessing fields. - _ = try dg.ctypeFromType(base_ty, .complete); - const payload_ty = switch (ptr.addr) { - .eu_payload => base_ty.errorUnionPayload(zcu), - .opt_payload => base_ty.optionalChild(zcu), - else => unreachable, - }; - const payload_ctype = try dg.ctypeFromType(payload_ty, .forward); - if (!ptr_child_ctype.eql(payload_ctype)) { - try writer.writeByte('('); - try dg.renderCType(writer, ptr_ctype); - try writer.writeByte(')'); - } + + .decl_ptr => |decl| try dg.renderDeclValue(writer, decl, location), + .anon_decl_ptr => |ad| try dg.renderAnonDeclValue(writer, ad, location), + + inline .eu_payload_ptr, .opt_payload_ptr => |info| { try writer.writeAll("&("); - try dg.renderParentPtr(writer, base, location); + try dg.renderPointer(writer, info.parent.*, location); try writer.writeAll(")->payload"); }, - .elem => |elem| { - const ptr_base_ty = Type.fromInterned(ip.typeOf(elem.base)); - const elem_ty = ptr_base_ty.elemType2(zcu); - const elem_ctype = try dg.ctypeFromType(elem_ty, .forward); - if (!ptr_child_ctype.eql(elem_ctype)) { - try writer.writeByte('('); - try dg.renderCType(writer, ptr_ctype); - try writer.writeByte(')'); - } - try writer.writeAll("&("); - if (ip.indexToKey(ptr_base_ty.toIntern()).ptr_type.flags.size == .One) - try writer.writeByte('*'); - try dg.renderParentPtr(writer, elem.base, location); - try writer.print(")[{d}]", .{elem.index}); - }, - .field => |field| { - const ptr_base_ty = Type.fromInterned(ip.typeOf(field.base)); - const base_ty = ptr_base_ty.childType(zcu); + + .field_ptr => |field| { + const parent_ptr_ty = try field.parent.ptrType(zcu); + // Ensure complete type definition is available before accessing fields. - _ = try dg.ctypeFromType(base_ty, .complete); - switch (fieldLocation(ptr_base_ty, ptr_ty, @as(u32, @intCast(field.index)), zcu)) { + _ = try dg.ctypeFromType(parent_ptr_ty.childType(zcu), .complete); + + switch (fieldLocation(parent_ptr_ty, field.result_ptr_ty, field.field_idx, zcu)) { .begin => { - const ptr_base_ctype = try dg.ctypeFromType(ptr_base_ty, .complete); - if (!ptr_ctype.eql(ptr_base_ctype)) { - try writer.writeByte('('); - try dg.renderCType(writer, ptr_ctype); - try writer.writeByte(')'); - } - try dg.renderParentPtr(writer, field.base, location); + const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete); + try writer.writeByte('('); + try dg.renderCType(writer, ptr_ctype); + try writer.writeByte(')'); + try dg.renderPointer(writer, field.parent.*, location); }, .field => |name| { - const field_ty = switch (ip.indexToKey(base_ty.toIntern())) { - .anon_struct_type, - .struct_type, - .union_type, - => base_ty.structFieldType(@as(usize, @intCast(field.index)), zcu), - .ptr_type => |ptr_type| switch (ptr_type.flags.size) { - .One, .Many, .C => unreachable, - .Slice => switch (field.index) { - Value.slice_ptr_index => base_ty.slicePtrFieldType(zcu), - Value.slice_len_index => Type.usize, - else => unreachable, - }, - }, - else => unreachable, - }; - const field_ctype = try dg.ctypeFromType(field_ty, .forward); - if (!ptr_child_ctype.eql(field_ctype)) { - try writer.writeByte('('); - try dg.renderCType(writer, ptr_ctype); - try writer.writeByte(')'); - } try writer.writeAll("&("); - try dg.renderParentPtr(writer, field.base, location); + try dg.renderPointer(writer, field.parent.*, location); try writer.writeAll(")->"); try dg.writeCValue(writer, name); }, .byte_offset => |byte_offset| { - const u8_ptr_ty = try zcu.adjustPtrTypeChild(ptr_ty, Type.u8); - const u8_ptr_ctype = try dg.ctypeFromType(u8_ptr_ty, .complete); - - if (!ptr_ctype.eql(u8_ptr_ctype)) { - try writer.writeByte('('); - try dg.renderCType(writer, ptr_ctype); - try writer.writeByte(')'); - } - try writer.writeAll("(("); - try dg.renderCType(writer, u8_ptr_ctype); + const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete); + try writer.writeByte('('); + try dg.renderCType(writer, ptr_ctype); try writer.writeByte(')'); - try dg.renderParentPtr(writer, field.base, location); - try writer.print(" + {})", .{ - try dg.fmtIntLiteral(try zcu.intValue(Type.usize, byte_offset), .Other), - }); + const offset_val = try zcu.intValue(Type.usize, byte_offset); + try writer.writeAll("((char *)"); + try dg.renderPointer(writer, field.parent.*, location); + try writer.print(" + {})", .{try dg.fmtIntLiteral(offset_val, .Other)}); }, } }, - .comptime_field, .comptime_alloc => unreachable, + + .elem_ptr => |elem| if (!(try elem.parent.ptrType(zcu)).childType(zcu).hasRuntimeBits(zcu)) { + // Element type is zero-bit, so lowers to `void`. The index is irrelevant; just cast the pointer. + const ptr_ctype = try dg.ctypeFromType(elem.result_ptr_ty, .complete); + try writer.writeByte('('); + try dg.renderCType(writer, ptr_ctype); + try writer.writeByte(')'); + try dg.renderPointer(writer, elem.parent.*, location); + } else { + const index_val = try zcu.intValue(Type.usize, elem.elem_idx); + // We want to do pointer arithmetic on a pointer to the element type. + // We might have a pointer-to-array. In this case, we must cast first. + const result_ctype = try dg.ctypeFromType(elem.result_ptr_ty, .complete); + const parent_ctype = try dg.ctypeFromType(try elem.parent.ptrType(zcu), .complete); + if (result_ctype.eql(parent_ctype)) { + // The pointer already has an appropriate type - just do the arithmetic. + try writer.writeByte('('); + try dg.renderPointer(writer, elem.parent.*, location); + try writer.print(" + {})", .{try dg.fmtIntLiteral(index_val, .Other)}); + } else { + // We probably have an array pointer `T (*)[n]`. Cast to an element pointer, + // and *then* apply the index. + try writer.writeAll("(("); + try dg.renderCType(writer, result_ctype); + try writer.writeByte(')'); + try dg.renderPointer(writer, elem.parent.*, location); + try writer.print(" + {})", .{try dg.fmtIntLiteral(index_val, .Other)}); + } + }, + + .offset_and_cast => |oac| { + const ptr_ctype = try dg.ctypeFromType(oac.new_ptr_ty, .complete); + try writer.writeByte('('); + try dg.renderCType(writer, ptr_ctype); + try writer.writeByte(')'); + if (oac.byte_offset == 0) { + try dg.renderPointer(writer, oac.parent.*, location); + } else { + const offset_val = try zcu.intValue(Type.usize, oac.byte_offset); + try writer.writeAll("((char *)"); + try dg.renderPointer(writer, oac.parent.*, location); + try writer.print(" + {})", .{try dg.fmtIntLiteral(offset_val, .Other)}); + } + }, } } @@ -1103,20 +1084,11 @@ pub const DeclGen = struct { } try writer.writeByte('}'); }, - .ptr => |ptr| switch (ptr.addr) { - .decl => |d| try dg.renderDeclValue(writer, val, d, location), - .anon_decl => |decl_val| try dg.renderAnonDeclValue(writer, val, decl_val, location), - .int => |int| { - try writer.writeAll("(("); - try dg.renderCType(writer, ctype); - try writer.print("){x})", .{try dg.fmtIntLiteral(Value.fromInterned(int), location)}); - }, - .eu_payload, - .opt_payload, - .elem, - .field, - => try dg.renderParentPtr(writer, val.toIntern(), location), - .comptime_field, .comptime_alloc => unreachable, + .ptr => { + var arena = std.heap.ArenaAllocator.init(zcu.gpa); + defer arena.deinit(); + const derivation = try val.pointerDerivation(arena.allocator(), zcu); + try dg.renderPointer(writer, derivation, location); }, .opt => |opt| switch (ctype.info(ctype_pool)) { .basic => if (ctype.isBool()) try writer.writeAll(switch (opt.val) { @@ -4574,10 +4546,10 @@ fn airCall( break :fn_decl switch (zcu.intern_pool.indexToKey(callee_val.toIntern())) { .extern_func => |extern_func| extern_func.decl, .func => |func| func.owner_decl, - .ptr => |ptr| switch (ptr.addr) { + .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { .decl => |decl| decl, else => break :known, - }, + } else break :known, else => break :known, }; }; @@ -5147,10 +5119,10 @@ fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool 'I' => !target.cpu.arch.isArmOrThumb(), else => switch (value) { .constant => |val| switch (f.object.dg.zcu.intern_pool.indexToKey(val.toIntern())) { - .ptr => |ptr| switch (ptr.addr) { + .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { .decl => false, else => true, - }, + } else true, else => true, }, else => false, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index db0eaa3ce5..8d2bca2f6c 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3262,6 +3262,7 @@ pub const Object = struct { try o.lowerType(Type.fromInterned(vector_type.child)), ), .opt_type => |child_ty| { + // Must stay in sync with `opt_payload` logic in `lowerPtr`. if (!Type.fromInterned(child_ty).hasRuntimeBitsIgnoreComptime(mod)) return .i8; const payload_ty = try o.lowerType(Type.fromInterned(child_ty)); @@ -3281,6 +3282,8 @@ pub const Object = struct { }, .anyframe_type => @panic("TODO implement lowerType for AnyFrame types"), .error_union_type => |error_union_type| { + // Must stay in sync with `codegen.errUnionPayloadOffset`. + // See logic in `lowerPtr`. const error_type = try o.errorIntType(); if (!Type.fromInterned(error_union_type.payload_type).hasRuntimeBitsIgnoreComptime(mod)) return error_type; @@ -3792,17 +3795,7 @@ pub const Object = struct { 128 => try o.builder.fp128Const(val.toFloat(f128, mod)), else => unreachable, }, - .ptr => |ptr| return switch (ptr.addr) { - .decl => |decl| try o.lowerDeclRefValue(ty, decl), - .anon_decl => |anon_decl| try o.lowerAnonDeclRef(ty, anon_decl), - .int => |int| try o.lowerIntAsPtr(int), - .eu_payload, - .opt_payload, - .elem, - .field, - => try o.lowerParentPtr(val), - .comptime_field, .comptime_alloc => unreachable, - }, + .ptr => try o.lowerPtr(arg_val, 0), .slice => |slice| return o.builder.structConst(try o.lowerType(ty), &.{ try o.lowerValue(slice.ptr), try o.lowerValue(slice.len), @@ -4223,20 +4216,6 @@ pub const Object = struct { }; } - fn lowerIntAsPtr(o: *Object, val: InternPool.Index) Allocator.Error!Builder.Constant { - const mod = o.module; - switch (mod.intern_pool.indexToKey(val)) { - .undef => return o.builder.undefConst(.ptr), - .int => { - var bigint_space: Value.BigIntSpace = undefined; - const bigint = Value.fromInterned(val).toBigInt(&bigint_space, mod); - const llvm_int = try lowerBigInt(o, Type.usize, bigint); - return o.builder.castConst(.inttoptr, llvm_int, .ptr); - }, - else => unreachable, - } - } - fn lowerBigInt( o: *Object, ty: Type, @@ -4246,129 +4225,60 @@ pub const Object = struct { return o.builder.bigIntConst(try o.builder.intType(ty.intInfo(mod).bits), bigint); } - fn lowerParentPtrDecl(o: *Object, decl_index: InternPool.DeclIndex) Allocator.Error!Builder.Constant { - const mod = o.module; - const decl = mod.declPtr(decl_index); - const ptr_ty = try mod.singleMutPtrType(decl.typeOf(mod)); - return o.lowerDeclRefValue(ptr_ty, decl_index); - } - - fn lowerParentPtr(o: *Object, ptr_val: Value) Error!Builder.Constant { - const mod = o.module; - const ip = &mod.intern_pool; - const ptr = ip.indexToKey(ptr_val.toIntern()).ptr; - return switch (ptr.addr) { - .decl => |decl| try o.lowerParentPtrDecl(decl), - .anon_decl => |ad| try o.lowerAnonDeclRef(Type.fromInterned(ad.orig_ty), ad), - .int => |int| try o.lowerIntAsPtr(int), - .eu_payload => |eu_ptr| { - const parent_ptr = try o.lowerParentPtr(Value.fromInterned(eu_ptr)); - - const eu_ty = Type.fromInterned(ip.typeOf(eu_ptr)).childType(mod); - const payload_ty = eu_ty.errorUnionPayload(mod); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { - // In this case, we represent pointer to error union the same as pointer - // to the payload. - return parent_ptr; - } - - const err_int_ty = try mod.errorIntType(); - const payload_align = payload_ty.abiAlignment(mod); - const err_align = err_int_ty.abiAlignment(mod); - const index: u32 = if (payload_align.compare(.gt, err_align)) 2 else 1; - return o.builder.gepConst(.inbounds, try o.lowerType(eu_ty), parent_ptr, null, &.{ - .@"0", try o.builder.intConst(.i32, index), + fn lowerPtr( + o: *Object, + ptr_val: InternPool.Index, + prev_offset: u64, + ) Error!Builder.Constant { + const zcu = o.module; + const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; + const offset: u64 = prev_offset + ptr.byte_offset; + return switch (ptr.base_addr) { + .decl => |decl| { + const base_ptr = try o.lowerDeclRefValue(decl); + return o.builder.gepConst(.inbounds, .i8, base_ptr, null, &.{ + try o.builder.intConst(.i64, offset), }); }, - .opt_payload => |opt_ptr| { - const parent_ptr = try o.lowerParentPtr(Value.fromInterned(opt_ptr)); - - const opt_ty = Type.fromInterned(ip.typeOf(opt_ptr)).childType(mod); - const payload_ty = opt_ty.optionalChild(mod); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod) or - payload_ty.optionalReprIsPayload(mod)) - { - // In this case, we represent pointer to optional the same as pointer - // to the payload. - return parent_ptr; - } - - return o.builder.gepConst(.inbounds, try o.lowerType(opt_ty), parent_ptr, null, &.{ .@"0", .@"0" }); - }, - .comptime_field, .comptime_alloc => unreachable, - .elem => |elem_ptr| { - const parent_ptr = try o.lowerParentPtr(Value.fromInterned(elem_ptr.base)); - const elem_ty = Type.fromInterned(ip.typeOf(elem_ptr.base)).elemType2(mod); - - return o.builder.gepConst(.inbounds, try o.lowerType(elem_ty), parent_ptr, null, &.{ - try o.builder.intConst(try o.lowerType(Type.usize), elem_ptr.index), + .anon_decl => |ad| { + const base_ptr = try o.lowerAnonDeclRef(ad); + return o.builder.gepConst(.inbounds, .i8, base_ptr, null, &.{ + try o.builder.intConst(.i64, offset), }); }, - .field => |field_ptr| { - const parent_ptr = try o.lowerParentPtr(Value.fromInterned(field_ptr.base)); - const parent_ptr_ty = Type.fromInterned(ip.typeOf(field_ptr.base)); - const parent_ty = parent_ptr_ty.childType(mod); - const field_index: u32 = @intCast(field_ptr.index); - switch (parent_ty.zigTypeTag(mod)) { - .Union => { - if (parent_ty.containerLayout(mod) == .@"packed") { - return parent_ptr; - } - - const layout = parent_ty.unionGetLayout(mod); - if (layout.payload_size == 0) { - // In this case a pointer to the union and a pointer to any - // (void) payload is the same. - return parent_ptr; - } - - const parent_llvm_ty = try o.lowerType(parent_ty); - return o.builder.gepConst(.inbounds, parent_llvm_ty, parent_ptr, null, &.{ - .@"0", - try o.builder.intConst(.i32, @intFromBool( - layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align), - )), - }); - }, - .Struct => { - if (mod.typeToPackedStruct(parent_ty)) |struct_type| { - const ptr_info = Type.fromInterned(ptr.ty).ptrInfo(mod); - if (ptr_info.packed_offset.host_size != 0) return parent_ptr; - - const parent_ptr_info = parent_ptr_ty.ptrInfo(mod); - const bit_offset = mod.structPackedFieldBitOffset(struct_type, field_index) + parent_ptr_info.packed_offset.bit_offset; - const llvm_usize = try o.lowerType(Type.usize); - const base_addr = try o.builder.castConst(.ptrtoint, parent_ptr, llvm_usize); - const byte_offset = try o.builder.intConst(llvm_usize, @divExact(bit_offset, 8)); - const field_addr = try o.builder.binConst(.add, base_addr, byte_offset); - return o.builder.castConst(.inttoptr, field_addr, .ptr); - } - - return o.builder.gepConst( - .inbounds, - try o.lowerType(parent_ty), - parent_ptr, - null, - if (o.llvmFieldIndex(parent_ty, field_index)) |llvm_field_index| &.{ - .@"0", - try o.builder.intConst(.i32, llvm_field_index), - } else &.{ - try o.builder.intConst(.i32, @intFromBool( - parent_ty.hasRuntimeBitsIgnoreComptime(mod), - )), - }, - ); + .int => try o.builder.castConst( + .inttoptr, + try o.builder.intConst(try o.lowerType(Type.usize), offset), + .ptr, + ), + .eu_payload => |eu_ptr| try o.lowerPtr( + eu_ptr, + offset + @import("../codegen.zig").errUnionPayloadOffset( + Value.fromInterned(eu_ptr).typeOf(zcu).childType(zcu), + zcu, + ), + ), + .opt_payload => |opt_ptr| try o.lowerPtr(opt_ptr, offset), + .field => |field| { + const agg_ty = Value.fromInterned(field.base).typeOf(zcu).childType(zcu); + const field_off: u64 = switch (agg_ty.zigTypeTag(zcu)) { + .Pointer => off: { + assert(agg_ty.isSlice(zcu)); + break :off switch (field.index) { + Value.slice_ptr_index => 0, + Value.slice_len_index => @divExact(zcu.getTarget().ptrBitWidth(), 8), + else => unreachable, + }; }, - .Pointer => { - assert(parent_ty.isSlice(mod)); - const parent_llvm_ty = try o.lowerType(parent_ty); - return o.builder.gepConst(.inbounds, parent_llvm_ty, parent_ptr, null, &.{ - .@"0", try o.builder.intConst(.i32, field_index), - }); + .Struct, .Union => switch (agg_ty.containerLayout(zcu)) { + .auto => agg_ty.structFieldOffset(@intCast(field.index), zcu), + .@"extern", .@"packed" => unreachable, }, else => unreachable, - } + }; + return o.lowerPtr(field.base, offset + field_off); }, + .arr_elem, .comptime_field, .comptime_alloc => unreachable, }; } @@ -4376,8 +4286,7 @@ pub const Object = struct { /// Maybe the logic could be unified. fn lowerAnonDeclRef( o: *Object, - ptr_ty: Type, - anon_decl: InternPool.Key.Ptr.Addr.AnonDecl, + anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl, ) Error!Builder.Constant { const mod = o.module; const ip = &mod.intern_pool; @@ -4393,6 +4302,8 @@ pub const Object = struct { @panic("TODO"); } + const ptr_ty = Type.fromInterned(anon_decl.orig_ty); + const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn; if ((!is_fn_body and !decl_ty.hasRuntimeBits(mod)) or (is_fn_body and mod.typeToFunc(decl_ty).?.is_generic)) return o.lowerPtrToVoid(ptr_ty); @@ -4400,9 +4311,8 @@ pub const Object = struct { if (is_fn_body) @panic("TODO"); - const orig_ty = Type.fromInterned(anon_decl.orig_ty); - const llvm_addr_space = toLlvmAddressSpace(orig_ty.ptrAddressSpace(mod), target); - const alignment = orig_ty.ptrAlignment(mod); + const llvm_addr_space = toLlvmAddressSpace(ptr_ty.ptrAddressSpace(mod), target); + const alignment = ptr_ty.ptrAlignment(mod); const llvm_global = (try o.resolveGlobalAnonDecl(decl_val, llvm_addr_space, alignment)).ptrConst(&o.builder).global; const llvm_val = try o.builder.convConst( @@ -4411,13 +4321,10 @@ pub const Object = struct { try o.builder.ptrType(llvm_addr_space), ); - return o.builder.convConst(if (ptr_ty.isAbiInt(mod)) switch (ptr_ty.intInfo(mod).signedness) { - .signed => .signed, - .unsigned => .unsigned, - } else .unneeded, llvm_val, try o.lowerType(ptr_ty)); + return o.builder.convConst(.unneeded, llvm_val, try o.lowerType(ptr_ty)); } - fn lowerDeclRefValue(o: *Object, ty: Type, decl_index: InternPool.DeclIndex) Allocator.Error!Builder.Constant { + fn lowerDeclRefValue(o: *Object, decl_index: InternPool.DeclIndex) Allocator.Error!Builder.Constant { const mod = o.module; // In the case of something like: @@ -4428,18 +4335,23 @@ pub const Object = struct { const decl = mod.declPtr(decl_index); if (decl.val.getFunction(mod)) |func| { if (func.owner_decl != decl_index) { - return o.lowerDeclRefValue(ty, func.owner_decl); + return o.lowerDeclRefValue(func.owner_decl); } } else if (decl.val.getExternFunc(mod)) |func| { if (func.decl != decl_index) { - return o.lowerDeclRefValue(ty, func.decl); + return o.lowerDeclRefValue(func.decl); } } const decl_ty = decl.typeOf(mod); + const ptr_ty = try decl.declPtrType(mod); + const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn; if ((!is_fn_body and !decl_ty.hasRuntimeBits(mod)) or - (is_fn_body and mod.typeToFunc(decl_ty).?.is_generic)) return o.lowerPtrToVoid(ty); + (is_fn_body and mod.typeToFunc(decl_ty).?.is_generic)) + { + return o.lowerPtrToVoid(ptr_ty); + } const llvm_global = if (is_fn_body) (try o.resolveLlvmFunction(decl_index)).ptrConst(&o.builder).global @@ -4452,10 +4364,7 @@ pub const Object = struct { try o.builder.ptrType(toLlvmAddressSpace(decl.@"addrspace", mod.getTarget())), ); - return o.builder.convConst(if (ty.isAbiInt(mod)) switch (ty.intInfo(mod).signedness) { - .signed => .signed, - .unsigned => .unsigned, - } else .unneeded, llvm_val, try o.lowerType(ty)); + return o.builder.convConst(.unneeded, llvm_val, try o.lowerType(ptr_ty)); } fn lowerPtrToVoid(o: *Object, ptr_ty: Type) Allocator.Error!Builder.Constant { diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 53ec59d531..ed04ee475b 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -863,7 +863,7 @@ const DeclGen = struct { const result_ty_id = try self.resolveType(ty, repr); const ip = &mod.intern_pool; - log.debug("lowering constant: ty = {}, val = {}", .{ ty.fmt(mod), val.fmtValue(mod) }); + log.debug("lowering constant: ty = {}, val = {}", .{ ty.fmt(mod), val.fmtValue(mod, null) }); if (val.isUndefDeep(mod)) { return self.spv.constUndef(result_ty_id); } @@ -983,10 +983,10 @@ const DeclGen = struct { const int_ty = ty.intTagType(mod); break :cache try self.constant(int_ty, int_val, repr); }, - .ptr => return self.constantPtr(ty, val), + .ptr => return self.constantPtr(val), .slice => |slice| { const ptr_ty = ty.slicePtrFieldType(mod); - const ptr_id = try self.constantPtr(ptr_ty, Value.fromInterned(slice.ptr)); + const ptr_id = try self.constantPtr(Value.fromInterned(slice.ptr)); const len_id = try self.constant(Type.usize, Value.fromInterned(slice.len), .indirect); return self.constructStruct( ty, @@ -1107,62 +1107,86 @@ const DeclGen = struct { return cacheable_id; } - fn constantPtr(self: *DeclGen, ptr_ty: Type, ptr_val: Value) Error!IdRef { + fn constantPtr(self: *DeclGen, ptr_val: Value) Error!IdRef { // TODO: Caching?? - const result_ty_id = try self.resolveType(ptr_ty, .direct); - const mod = self.module; + const zcu = self.module; + + if (ptr_val.isUndef(zcu)) { + const result_ty = ptr_val.typeOf(zcu); + const result_ty_id = try self.resolveType(result_ty, .direct); + return self.spv.constUndef(result_ty_id); + } - if (ptr_val.isUndef(mod)) return self.spv.constUndef(result_ty_id); + var arena = std.heap.ArenaAllocator.init(self.gpa); + defer arena.deinit(); - switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) { - .decl => |decl| return try self.constantDeclRef(ptr_ty, decl), - .anon_decl => |anon_decl| return try self.constantAnonDeclRef(ptr_ty, anon_decl), + const derivation = try ptr_val.pointerDerivation(arena.allocator(), zcu); + return self.derivePtr(derivation); + } + + fn derivePtr(self: *DeclGen, derivation: Value.PointerDeriveStep) Error!IdRef { + const zcu = self.module; + switch (derivation) { + .comptime_alloc_ptr, .comptime_field_ptr => unreachable, .int => |int| { - const ptr_id = self.spv.allocId(); + const result_ty_id = try self.resolveType(int.ptr_ty, .direct); // TODO: This can probably be an OpSpecConstantOp Bitcast, but // that is not implemented by Mesa yet. Therefore, just generate it // as a runtime operation. + const result_ptr_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{ .id_result_type = result_ty_id, - .id_result = ptr_id, - .integer_value = try self.constant(Type.usize, Value.fromInterned(int), .direct), + .id_result = result_ptr_id, + .integer_value = try self.constant(Type.usize, try zcu.intValue(Type.usize, int.addr), .direct), }); - return ptr_id; + return result_ptr_id; + }, + .decl_ptr => |decl| { + const result_ptr_ty = try zcu.declPtr(decl).declPtrType(zcu); + return self.constantDeclRef(result_ptr_ty, decl); }, - .eu_payload => unreachable, // TODO - .opt_payload => unreachable, // TODO - .comptime_field, .comptime_alloc => unreachable, - .elem => |elem_ptr| { - const parent_ptr_ty = Type.fromInterned(mod.intern_pool.typeOf(elem_ptr.base)); - const parent_ptr_id = try self.constantPtr(parent_ptr_ty, Value.fromInterned(elem_ptr.base)); - const index_id = try self.constInt(Type.usize, elem_ptr.index, .direct); - - const elem_ptr_id = try self.ptrElemPtr(parent_ptr_ty, parent_ptr_id, index_id); - - // TODO: Can we consolidate this in ptrElemPtr? - const elem_ty = parent_ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T. - const elem_ptr_ty_id = try self.ptrType(elem_ty, self.spvStorageClass(parent_ptr_ty.ptrAddressSpace(mod))); - - // TODO: Can we remove this ID comparison? - if (elem_ptr_ty_id == result_ty_id) { - return elem_ptr_id; + .anon_decl_ptr => |ad| { + const result_ptr_ty = Type.fromInterned(ad.orig_ty); + return self.constantAnonDeclRef(result_ptr_ty, ad); + }, + .eu_payload_ptr => @panic("TODO"), + .opt_payload_ptr => @panic("TODO"), + .field_ptr => |field| { + const parent_ptr_id = try self.derivePtr(field.parent.*); + const parent_ptr_ty = try field.parent.ptrType(zcu); + return self.structFieldPtr(field.result_ptr_ty, parent_ptr_ty, parent_ptr_id, field.field_idx); + }, + .elem_ptr => |elem| { + const parent_ptr_id = try self.derivePtr(elem.parent.*); + const parent_ptr_ty = try elem.parent.ptrType(zcu); + const index_id = try self.constInt(Type.usize, elem.elem_idx, .direct); + return self.ptrElemPtr(parent_ptr_ty, parent_ptr_id, index_id); + }, + .offset_and_cast => |oac| { + const parent_ptr_id = try self.derivePtr(oac.parent.*); + const parent_ptr_ty = try oac.parent.ptrType(zcu); + disallow: { + if (oac.byte_offset != 0) break :disallow; + // Allow changing the pointer type child only to restructure arrays. + // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T. + const src_base_ty = parent_ptr_ty.arrayBase(zcu)[0]; + const dest_base_ty = oac.new_ptr_ty.arrayBase(zcu)[0]; + if (self.getTarget().os.tag == .vulkan and src_base_ty.toIntern() != dest_base_ty.toIntern()) break :disallow; + + const result_ty_id = try self.resolveType(oac.new_ptr_ty, .direct); + const result_ptr_id = self.spv.allocId(); + try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ + .id_result_type = result_ty_id, + .id_result = result_ptr_id, + .operand = parent_ptr_id, + }); + return result_ptr_id; } - // This may happen when we have pointer-to-array and the result is - // another pointer-to-array instead of a pointer-to-element. - const result_id = self.spv.allocId(); - try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ - .id_result_type = result_ty_id, - .id_result = result_id, - .operand = elem_ptr_id, + return self.fail("Cannot perform pointer cast: '{}' to '{}'", .{ + parent_ptr_ty.fmt(zcu), + oac.new_ptr_ty.fmt(zcu), }); - return result_id; - }, - .field => |field| { - const base_ptr_ty = Type.fromInterned(mod.intern_pool.typeOf(field.base)); - const base_ptr = try self.constantPtr(base_ptr_ty, Value.fromInterned(field.base)); - const field_index: u32 = @intCast(field.index); - return try self.structFieldPtr(ptr_ty, base_ptr_ty, base_ptr, field_index); }, } } @@ -1170,7 +1194,7 @@ const DeclGen = struct { fn constantAnonDeclRef( self: *DeclGen, ty: Type, - anon_decl: InternPool.Key.Ptr.Addr.AnonDecl, + anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl, ) !IdRef { // TODO: Merge this function with constantDeclRef. @@ -4456,16 +4480,20 @@ const DeclGen = struct { ) !IdRef { const result_ty_id = try self.resolveType(result_ptr_ty, .direct); - const mod = self.module; - const object_ty = object_ptr_ty.childType(mod); - switch (object_ty.zigTypeTag(mod)) { - .Struct => switch (object_ty.containerLayout(mod)) { + const zcu = self.module; + const object_ty = object_ptr_ty.childType(zcu); + switch (object_ty.zigTypeTag(zcu)) { + .Pointer => { + assert(object_ty.isSlice(zcu)); + return self.accessChain(result_ty_id, object_ptr, &.{field_index}); + }, + .Struct => switch (object_ty.containerLayout(zcu)) { .@"packed" => unreachable, // TODO else => { return try self.accessChain(result_ty_id, object_ptr, &.{field_index}); }, }, - .Union => switch (object_ty.containerLayout(mod)) { + .Union => switch (object_ty.containerLayout(zcu)) { .@"packed" => unreachable, // TODO else => { const layout = self.unionLayout(object_ty); @@ -4475,7 +4503,7 @@ const DeclGen = struct { return try self.spv.constUndef(result_ty_id); } - const storage_class = self.spvStorageClass(object_ptr_ty.ptrAddressSpace(mod)); + const storage_class = self.spvStorageClass(object_ptr_ty.ptrAddressSpace(zcu)); const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, storage_class); const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index}); |
