Commit 5d429b03e3

Robin Voetter <robin@voetter.nl>
2022-08-22 00:21:31
stage2: add @addrSpaceCast builtin
1 parent 5f3b914
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),
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),
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"),
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)"),
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,
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),
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),
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,
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,
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),
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",
             .{
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 });
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),
         };
     };
 
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,
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);
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,
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,