diff --git a/src/Air.zig b/src/Air.zig index 46ba297003..57479af590 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -729,6 +729,10 @@ pub const Inst = struct { /// Sets the operand as the current error return trace, set_err_return_trace, + /// Convert the address space of a pointer. + /// Uses the `ty_op` field. + addrspace_cast, + pub fn fromCmpOp(op: std.math.CompareOperator, optimized: bool) Tag { switch (op) { .lt => return if (optimized) .cmp_lt_optimized else .cmp_lt, @@ -1138,6 +1142,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .popcount, .byte_swap, .bit_reverse, + .addrspace_cast, => return air.getRefType(datas[inst].ty_op.ty), .loop, diff --git a/src/AstGen.zig b/src/AstGen.zig index 7534a0d2cc..7bb2ef765c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7789,6 +7789,14 @@ fn builtinCall( }); return rvalue(gz, rl, result, node); }, + .addrspace_cast => { + const result = try gz.addExtendedPayload(.addrspace_cast, Zir.Inst.BinNode{ + .lhs = try comptimeExpr(gz, scope, .{ .ty = .address_space_type }, params[0]), + .rhs = try expr(gz, scope, .none, params[1]), + .node = gz.nodeIndexToRelative(node), + }); + return rvalue(gz, rl, result, node); + }, // zig fmt: off .has_decl => return hasDeclOrField(gz, scope, rl, node, params[0], params[1], .has_decl), diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig index 3a13dde1ab..eb878873a0 100644 --- a/src/BuiltinFn.zig +++ b/src/BuiltinFn.zig @@ -2,6 +2,7 @@ const std = @import("std"); pub const Tag = enum { add_with_overflow, + addrspace_cast, align_cast, align_of, as, @@ -152,6 +153,13 @@ pub const list = list: { .param_count = 4, }, }, + .{ + "@addrSpaceCast", + .{ + .tag = .addrspace_cast, + .param_count = 2, + }, + }, .{ "@alignCast", .{ diff --git a/src/Liveness.zig b/src/Liveness.zig index 5a4bd2265e..54a5041e8b 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -268,6 +268,7 @@ pub fn categorizeOperand( .bit_reverse, .splat, .error_set_has_value, + .addrspace_cast, => { const o = air_datas[inst].ty_op; if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); @@ -844,6 +845,7 @@ fn analyzeInst( .bit_reverse, .splat, .error_set_has_value, + .addrspace_cast, => { const o = inst_datas[inst].ty_op; return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none }); diff --git a/src/Module.zig b/src/Module.zig index 44502ab564..7d87bdba53 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4617,7 +4617,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { .constant => target_util.defaultAddressSpace(target, .global_constant), else => unreachable, }, - else => |addrspace_ref| try sema.analyzeAddrspace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx), + else => |addrspace_ref| try sema.analyzeAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx), }; }; diff --git a/src/Sema.zig b/src/Sema.zig index b357beafdf..9a6c2acb14 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -975,8 +975,9 @@ fn analyzeBodyInner( .reify => try sema.zirReify( block, extended, inst), .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended), .cmpxchg => try sema.zirCmpxchg( block, extended), - + .addrspace_cast => try sema.zirAddrSpaceCast( block, extended), // zig fmt: on + .fence => { try sema.zirFence(block, extended); i += 1; @@ -16250,7 +16251,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const address_space = if (inst_data.flags.has_addrspace) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - break :blk try sema.analyzeAddrspace(block, addrspace_src, ref, .pointer); + break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer); } else .generic; const bit_offset = if (inst_data.flags.has_bit_range) blk: { @@ -18170,6 +18171,53 @@ fn reifyStruct( return sema.analyzeDeclVal(block, src, new_decl_index); } +fn zirAddrSpaceCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { + const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; + const src = LazySrcLoc.nodeOffset(extra.node); + const addrspace_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; + const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; + + const dest_addrspace = try sema.analyzeAddressSpace(block, addrspace_src, extra.lhs, .pointer); + const ptr = try sema.resolveInst(extra.rhs); + const ptr_ty = sema.typeOf(ptr); + + // TODO in addition to pointers, this instruction is supposed to work for + // pointer-like optionals and slices. + try sema.checkPtrOperand(block, ptr_src, ptr_ty); + + // TODO check address space cast validity. + const src_addrspace = ptr_ty.ptrAddressSpace(); + _ = src_addrspace; + + const ptr_info = ptr_ty.ptrInfo().data; + const dest_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = ptr_info.pointee_type, + .@"align" = ptr_info.@"align", + .@"addrspace" = dest_addrspace, + .mutable = ptr_info.mutable, + .@"allowzero" = ptr_info.@"allowzero", + .@"volatile" = ptr_info.@"volatile", + .size = ptr_info.size, + }); + + if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |val| { + // Pointer value should compatible with both address spaces. + // TODO: Figure out why this generates an invalid bitcast. + return sema.addConstant(dest_ty, val); + } + + try sema.requireRuntimeBlock(block, src, ptr_src); + // TODO: Address space cast safety? + + return block.addInst(.{ + .tag = .addrspace_cast, + .data = .{ .ty_op = .{ + .ty = try sema.addType(dest_ty), + .operand = ptr, + } }, + }); +} + fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; @@ -30292,7 +30340,7 @@ pub const AddressSpaceContext = enum { pointer, }; -pub fn analyzeAddrspace( +pub fn analyzeAddressSpace( sema: *Sema, block: *Block, src: LazySrcLoc, diff --git a/src/Zir.zig b/src/Zir.zig index 890109fcb0..351330b7c4 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1969,6 +1969,9 @@ pub const Inst = struct { /// `small` 0=>weak 1=>strong /// `operand` is payload index to `Cmpxchg`. cmpxchg, + /// Implement the builtin `@addrSpaceCast` + /// `Operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand. + addrspace_cast, pub const InstData = struct { opcode: Extended, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index ed3a281b80..2758fd36df 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -677,6 +677,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .union_init => try self.airUnionInit(inst), .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}), .@"try" => try self.airTry(inst), .try_ptr => try self.airTryPtr(inst), diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 95dfb2eea3..855951f5fa 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -690,6 +690,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .union_init => try self.airUnionInit(inst), .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}), .@"try" => try self.airTry(inst), .try_ptr => try self.airTryPtr(inst), diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 87e81748f9..dd31bfb6f7 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -604,6 +604,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .union_init => try self.airUnionInit(inst), .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .addrspace_cast => @panic("TODO"), .@"try" => @panic("TODO"), .try_ptr => @panic("TODO"), diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index cfcfedf7cc..6217119f34 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -618,6 +618,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .union_init => @panic("TODO try self.airUnionInit(inst)"), .prefetch => try self.airPrefetch(inst), .mul_add => @panic("TODO try self.airMulAdd(inst)"), + .addrspace_cast => @panic("TODO try self.airAddrSpaceCast(int)"), .@"try" => try self.airTry(inst), .try_ptr => @panic("TODO try self.airTryPtr(inst)"), diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index f27957d3f5..619addfba1 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1699,6 +1699,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .set_err_return_trace, .is_named_enum_value, .error_set_has_value, + .addrspace_cast, => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), .add_optimized, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 27eb11c649..abba07b0e8 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -695,6 +695,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .union_init => try self.airUnionInit(inst), .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}), .@"try" => try self.airTry(inst), .try_ptr => try self.airTryPtr(inst), diff --git a/src/codegen/c.zig b/src/codegen/c.zig index b25e05e118..072091d9b2 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1871,6 +1871,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .aggregate_init => try airAggregateInit(f, inst), .union_init => try airUnionInit(f, inst), .prefetch => try airPrefetch(f, inst), + .addrspace_cast => return f.fail("TODO: C backend: implement addrspace_cast", .{}), .@"try" => try airTry(f, inst), .try_ptr => try airTryPtr(f, inst), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index f400e841ea..aebd7a7dd0 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4512,6 +4512,7 @@ pub const FuncGen = struct { .aggregate_init => try self.airAggregateInit(inst), .union_init => try self.airUnionInit(inst), .prefetch => try self.airPrefetch(inst), + .addrspace_cast => try self.airAddrSpaceCast(inst), .is_named_enum_value => try self.airIsNamedEnumValue(inst), .error_set_has_value => try self.airErrorSetHasValue(inst), @@ -9045,6 +9046,17 @@ pub const FuncGen = struct { return null; } + fn airAddrSpaceCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const inst_ty = self.air.typeOfIndex(inst); + const operand = try self.resolveInst(ty_op.operand); + + const llvm_dest_ty = try self.dg.lowerType(inst_ty); + return self.builder.buildAddrSpaceCast(operand, llvm_dest_ty, ""); + } + fn softF80TruncOrExt( self: *FuncGen, operand: *llvm.Value, diff --git a/src/print_air.zig b/src/print_air.zig index fb6f7e6cf2..d3523c0fc6 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -244,6 +244,7 @@ const Writer = struct { .byte_swap, .bit_reverse, .error_set_has_value, + .addrspace_cast, => try w.writeTyOp(s, inst), .block, diff --git a/src/print_zir.zig b/src/print_zir.zig index 8f055e9ddd..f2a79d53a4 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -512,6 +512,7 @@ const Writer = struct { .err_set_cast, .wasm_memory_grow, .prefetch, + .addrspace_cast, => { const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(inst_data.node);