Commit 805e1bffbd

Robin Voetter <robin@voetter.nl>
2021-09-02 14:46:31
Address Spaces: Sema basics
1 parent 7da9fa6
lib/std/builtin.zig
@@ -170,6 +170,7 @@ pub const CallingConvention = enum {
 /// therefore must be kept in sync with the compiler implementation.
 pub const AddressSpace = enum {
     generic,
+    special,
 };
 
 /// This data structure is used by the Zig language code generation and
src/AstGen.zig
@@ -3134,7 +3134,7 @@ fn fnDecl(
     _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
     try decl_gz.setBlockBody(block_inst);
 
-    try wip_decls.payload.ensureUnusedCapacity(gpa, 9);
+    try wip_decls.payload.ensureUnusedCapacity(gpa, 10);
     {
         const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
         const casted = @bitCast([4]u32, contents_hash);
@@ -3284,7 +3284,7 @@ fn globalVarDecl(
     _ = try block_scope.addBreak(.break_inline, block_inst, var_inst);
     try block_scope.setBlockBody(block_inst);
 
-    try wip_decls.payload.ensureUnusedCapacity(gpa, 9);
+    try wip_decls.payload.ensureUnusedCapacity(gpa, 10);
     {
         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
         const casted = @bitCast([4]u32, contents_hash);
src/Module.zig
@@ -288,6 +288,8 @@ pub const Decl = struct {
     align_val: Value,
     /// Populated when `has_tv`.
     linksection_val: Value,
+    /// Populated when `has_tv`.
+    @"addrspace": std.builtin.AddressSpace,
     /// The memory for ty, val, align_val, linksection_val.
     /// If this is `null` then there is no memory management needed.
     value_arena: ?*std.heap.ArenaAllocator.State = null,
@@ -351,7 +353,7 @@ pub const Decl = struct {
         /// to require re-analysis.
         outdated,
     },
-    /// Whether `typed_value`, `align_val`, and `linksection_val` are populated.
+    /// Whether `typed_value`, `align_val`, `linksection_val` and `has_addrspace` are populated.
     has_tv: bool,
     /// If `true` it means the `Decl` is the resource owner of the type/value associated
     /// with it. That means when `Decl` is destroyed, the cleanup code should additionally
@@ -366,8 +368,8 @@ pub const Decl = struct {
     is_exported: bool,
     /// Whether the ZIR code provides an align instruction.
     has_align: bool,
-    /// Whether the ZIR code provides a linksection instruction.
-    has_linksection: bool,
+    /// Whether the ZIR code provides a linksection and address space instruction.
+    has_linksection_or_addrspace: bool,
     /// Flag used by garbage collection to mark and sweep.
     /// Decls which correspond to an AST node always have this field set to `true`.
     /// Anonymous Decls are initialized with this field set to `false` and then it
@@ -489,14 +491,22 @@ pub const Decl = struct {
         if (!decl.has_align) return .none;
         assert(decl.zir_decl_index != 0);
         const zir = decl.namespace.file_scope.zir;
-        return @intToEnum(Zir.Inst.Ref, zir.extra[decl.zir_decl_index + 6]);
+        return @intToEnum(Zir.Inst.Ref, zir.extra[decl.zir_decl_index + 7]);
     }
 
     pub fn zirLinksectionRef(decl: Decl) Zir.Inst.Ref {
-        if (!decl.has_linksection) return .none;
+        if (!decl.has_linksection_or_addrspace) return .none;
         assert(decl.zir_decl_index != 0);
         const zir = decl.namespace.file_scope.zir;
-        const extra_index = decl.zir_decl_index + 6 + @boolToInt(decl.has_align);
+        const extra_index = decl.zir_decl_index + 7 + @boolToInt(decl.has_align);
+        return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
+    }
+
+    pub fn zirAddrspaceRef(decl: Decl) Zir.Inst.Ref {
+        if (!decl.has_linksection_or_addrspace) return .none;
+        assert(decl.zir_decl_index != 0);
+        const zir = decl.namespace.file_scope.zir;
+        const extra_index = decl.zir_decl_index + 7 + @boolToInt(decl.has_align) + 1;
         return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
     }
 
@@ -3072,7 +3082,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
     new_decl.is_pub = true;
     new_decl.is_exported = false;
     new_decl.has_align = false;
-    new_decl.has_linksection = false;
+    new_decl.has_linksection_or_addrspace = false;
     new_decl.ty = struct_ty;
     new_decl.val = struct_val;
     new_decl.has_tv = true;
@@ -3202,6 +3212,12 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
         if (linksection_ref == .none) break :blk Value.initTag(.null_value);
         break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val;
     };
+    const address_space = blk: {
+        const addrspace_ref = decl.zirAddrspaceRef();
+        if (addrspace_ref == .none) break :blk .generic;
+        const addrspace_tv = try sema.resolveInstConst(&block_scope, src, addrspace_ref);
+        break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace);
+    };
     // Note this resolves the type of the Decl, not the value; if this Decl
     // is a struct, for example, this resolves `type` (which needs no resolution),
     // not the struct itself.
@@ -3258,6 +3274,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
             decl.val = try decl_tv.val.copy(&decl_arena.allocator);
             decl.align_val = try align_val.copy(&decl_arena.allocator);
             decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
+            decl.@"addrspace" = address_space;
             decl.has_tv = true;
             decl.owns_tv = owns_tv;
             decl_arena_state.* = decl_arena.state;
@@ -3319,6 +3336,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
     decl.val = try decl_tv.val.copy(&decl_arena.allocator);
     decl.align_val = try align_val.copy(&decl_arena.allocator);
     decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
+    decl.@"addrspace" = address_space;
     decl.has_tv = true;
     decl_arena_state.* = decl_arena.state;
     decl.value_arena = decl_arena_state;
@@ -3526,8 +3544,8 @@ pub fn scanNamespace(
 
         const decl_sub_index = extra_index;
         extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1)
-        extra_index += @truncate(u1, flags >> 2);
-        extra_index += @truncate(u1, flags >> 3);
+        extra_index += @truncate(u1, flags >> 2); // Align
+        extra_index += @as(u2, @truncate(u1, flags >> 3)) * 2; // Link section or address space, consists of 2 Refs
 
         try scanDecl(&scan_decl_iter, decl_sub_index, flags);
     }
@@ -3553,10 +3571,10 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
     const zir = namespace.file_scope.zir;
 
     // zig fmt: off
-    const is_pub          = (flags & 0b0001) != 0;
-    const export_bit      = (flags & 0b0010) != 0;
-    const has_align       = (flags & 0b0100) != 0;
-    const has_linksection = (flags & 0b1000) != 0;
+    const is_pub                       = (flags & 0b0001) != 0;
+    const export_bit                   = (flags & 0b0010) != 0;
+    const has_align                    = (flags & 0b0100) != 0;
+    const has_linksection_or_addrspace = (flags & 0b1000) != 0;
     // zig fmt: on
 
     const line = iter.parent_decl.relativeToLine(zir.extra[decl_sub_index + 4]);
@@ -3639,7 +3657,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
         new_decl.is_exported = is_exported;
         new_decl.is_usingnamespace = is_usingnamespace;
         new_decl.has_align = has_align;
-        new_decl.has_linksection = has_linksection;
+        new_decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
         new_decl.zir_decl_index = @intCast(u32, decl_sub_index);
         new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive.
         return;
@@ -3656,7 +3674,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
     decl.is_exported = is_exported;
     decl.is_usingnamespace = is_usingnamespace;
     decl.has_align = has_align;
-    decl.has_linksection = has_linksection;
+    decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
     decl.zir_decl_index = @intCast(u32, decl_sub_index);
     if (decl.getFunction()) |_| {
         switch (mod.comp.bin_file.tag) {
@@ -4028,6 +4046,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
         .val = undefined,
         .align_val = undefined,
         .linksection_val = undefined,
+        .@"addrspace" = undefined,
         .analysis = .unreferenced,
         .deletion_flag = false,
         .zir_decl_index = 0,
@@ -4052,7 +4071,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
         .generation = 0,
         .is_pub = false,
         .is_exported = false,
-        .has_linksection = false,
+        .has_linksection_or_addrspace = false,
         .has_align = false,
         .alive = false,
         .is_usingnamespace = false,
@@ -4357,6 +4376,7 @@ pub fn ptrType(
     elem_ty: Type,
     sentinel: ?Value,
     @"align": u32,
+    @"addrspace": std.builtin.AddressSpace,
     bit_offset: u16,
     host_size: u16,
     mutable: bool,
@@ -4371,6 +4391,7 @@ pub fn ptrType(
         .pointee_type = elem_ty,
         .sentinel = sentinel,
         .@"align" = @"align",
+        .@"addrspace" = @"addrspace",
         .bit_offset = bit_offset,
         .host_size = host_size,
         .@"allowzero" = @"allowzero",
src/Sema.zig
@@ -3004,7 +3004,7 @@ fn analyzeCall(
             new_decl.is_pub = module_fn.owner_decl.is_pub;
             new_decl.is_exported = module_fn.owner_decl.is_exported;
             new_decl.has_align = module_fn.owner_decl.has_align;
-            new_decl.has_linksection = module_fn.owner_decl.has_linksection;
+            new_decl.has_linksection_or_addrspace = module_fn.owner_decl.has_linksection_or_addrspace;
             new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index;
             new_decl.alive = true; // This Decl is called at runtime.
             new_decl.has_tv = true;
@@ -3895,6 +3895,7 @@ fn zirFunc(
         ret_ty_body,
         cc,
         Value.initTag(.null_value),
+        .generic,
         false,
         inferred_error_set,
         false,
@@ -3911,6 +3912,7 @@ fn funcCommon(
     ret_ty_body: []const Zir.Inst.Index,
     cc: std.builtin.CallingConvention,
     align_val: Value,
+    address_space: std.builtin.AddressSpace,
     var_args: bool,
     inferred_error_set: bool,
     is_extern: bool,
@@ -3968,7 +3970,7 @@ fn funcCommon(
         // Hot path for some common function types.
         // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.
         if (!is_generic and block.params.items.len == 0 and !var_args and
-            align_val.tag() == .null_value and !inferred_error_set)
+            align_val.tag() == .null_value and !inferred_error_set and address_space == .generic)
         {
             if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
                 break :fn_ty Type.initTag(.fn_noreturn_no_args);
@@ -4020,6 +4022,7 @@ fn funcCommon(
             .comptime_params = comptime_params.ptr,
             .return_type = return_type,
             .cc = cc,
+            .@"addrspace" = address_space,
             .is_var_args = var_args,
             .is_generic = is_generic,
         });
@@ -6876,6 +6879,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp
         elem_type,
         null,
         0,
+        .generic,
         0,
         0,
         inst_data.is_mutable,
@@ -6908,6 +6912,13 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
         break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32);
     } else 0;
 
+    const address_space = if (inst_data.flags.has_addrspace) blk: {
+        const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
+        extra_i += 1;
+        const addrspace_tv = try sema.resolveInstConst(block, .unneeded, ref);
+        break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace);
+    } else .generic;
+
     const bit_start = if (inst_data.flags.has_bit_range) blk: {
         const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
         extra_i += 1;
@@ -6930,6 +6941,7 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
         elem_type,
         sentinel,
         abi_align,
+        address_space,
         bit_start,
         bit_end,
         inst_data.flags.is_mutable,
@@ -8035,6 +8047,7 @@ fn zirFuncExtended(
     const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
     const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = extra.data.src_node };
     const align_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at align
+    const addrspace_src: LazySrcLoc = src; // TODO(Snektron) add a LazySrcLoc that points at addrspace
     const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small);
 
     var extra_index: usize = extra.end;
@@ -8059,6 +8072,13 @@ fn zirFuncExtended(
         break :blk align_tv.val;
     } else Value.initTag(.null_value);
 
+    const address_space: std.builtin.AddressSpace = if (small.has_addrspace) blk: {
+        const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+        extra_index += 1;
+        const addrspace_tv = try sema.resolveInstConst(block, addrspace_src, addrspace_ref);
+        break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace);
+    } else .generic;
+
     const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len];
     extra_index += ret_ty_body.len;
 
@@ -8081,6 +8101,7 @@ fn zirFuncExtended(
         ret_ty_body,
         cc,
         align_val,
+        address_space,
         is_var_args,
         is_inferred_error,
         is_extern,
@@ -9693,6 +9714,9 @@ fn analyzeSlice(
         return_elem_type,
         if (end_opt == .none) slice_sentinel else null,
         0, // TODO alignment
+        // TODO(Snektron) address space, should be inferred from the pointer type.
+        // TODO(Snektron) address space for slicing a local, should compute address space from context and architecture.
+        .generic,
         0,
         0,
         !ptr_child.isConstPtr(),
src/type.zig
@@ -289,6 +289,7 @@ pub const Type = extern union {
                 .pointee_type = Type.initTag(.comptime_int),
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -300,6 +301,7 @@ pub const Type = extern union {
                 .pointee_type = Type.initTag(.u8),
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -311,6 +313,7 @@ pub const Type = extern union {
                 .pointee_type = self.castPointer().?.data,
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -322,6 +325,7 @@ pub const Type = extern union {
                 .pointee_type = self.castPointer().?.data,
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -333,6 +337,7 @@ pub const Type = extern union {
                 .pointee_type = self.castPointer().?.data,
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -344,6 +349,7 @@ pub const Type = extern union {
                 .pointee_type = Type.initTag(.u8),
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -355,6 +361,7 @@ pub const Type = extern union {
                 .pointee_type = self.castPointer().?.data,
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -366,6 +373,7 @@ pub const Type = extern union {
                 .pointee_type = Type.initTag(.u8),
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -377,6 +385,7 @@ pub const Type = extern union {
                 .pointee_type = self.castPointer().?.data,
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -388,6 +397,7 @@ pub const Type = extern union {
                 .pointee_type = self.castPointer().?.data,
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -399,6 +409,7 @@ pub const Type = extern union {
                 .pointee_type = self.castPointer().?.data,
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -410,6 +421,7 @@ pub const Type = extern union {
                 .pointee_type = self.castPointer().?.data,
                 .sentinel = null,
                 .@"align" = 0,
+                .@"addrspace" = .generic,
                 .bit_offset = 0,
                 .host_size = 0,
                 .@"allowzero" = false,
@@ -462,6 +474,8 @@ pub const Type = extern union {
                     return false;
                 if (info_a.host_size != info_b.host_size)
                     return false;
+                if (info_a.@"addrspace" != info_b.@"addrspace")
+                    return false;
 
                 const sentinel_a = info_a.sentinel;
                 const sentinel_b = info_b.sentinel;
@@ -516,6 +530,8 @@ pub const Type = extern union {
                     return false;
                 if (a.fnCallingConvention() != b.fnCallingConvention())
                     return false;
+                if (a.fnAddressSpace() != b.fnAddressSpace())
+                    return false;
                 const a_param_len = a.fnParamLen();
                 const b_param_len = b.fnParamLen();
                 if (a_param_len != b_param_len)
@@ -822,6 +838,7 @@ pub const Type = extern union {
                     .return_type = try payload.return_type.copy(allocator),
                     .param_types = param_types,
                     .cc = payload.cc,
+                    .@"addrspace" = payload.@"addrspace",
                     .is_var_args = payload.is_var_args,
                     .is_generic = payload.is_generic,
                     .comptime_params = comptime_params.ptr,
@@ -837,6 +854,7 @@ pub const Type = extern union {
                     .pointee_type = try payload.pointee_type.copy(allocator),
                     .sentinel = sent,
                     .@"align" = payload.@"align",
+                    .@"addrspace" = payload.@"addrspace",
                     .bit_offset = payload.bit_offset,
                     .host_size = payload.host_size,
                     .@"allowzero" = payload.@"allowzero",
@@ -983,6 +1001,9 @@ pub const Type = extern union {
                     try writer.writeAll(") callconv(.");
                     try writer.writeAll(@tagName(payload.cc));
                     try writer.writeAll(") ");
+                    if (payload.@"addrspace" != .generic) {
+                        try writer.print("addrspace(.{s}) ", .{ @tagName(payload.@"addrspace") });
+                    }
                     ty = payload.return_type;
                     continue;
                 },
@@ -1114,6 +1135,9 @@ pub const Type = extern union {
                         }
                         try writer.writeAll(") ");
                     }
+                    if (payload.@"addrspace" != .generic) {
+                        try writer.print("addrspace(.{s}) ", .{ @tagName(payload.@"addrspace") });
+                    }
                     if (!payload.mutable) try writer.writeAll("const ");
                     if (payload.@"volatile") try writer.writeAll("volatile ");
                     if (payload.@"allowzero") try writer.writeAll("allowzero ");
@@ -2642,6 +2666,18 @@ pub const Type = extern union {
         };
     }
 
+    pub fn fnAddressSpace(self: Type) std.builtin.AddressSpace {
+        return switch (self.tag()) {
+            .fn_noreturn_no_args => .generic,
+            .fn_void_no_args => .generic,
+            .fn_naked_noreturn_no_args => .generic,
+            .fn_ccc_void_no_args => .generic,
+            .function => self.castTag(.function).?.data.@"addrspace",
+
+            else => unreachable,
+        };
+    }
+
     pub fn fnInfo(ty: Type) Payload.Function.Data {
         return switch (ty.tag()) {
             .fn_noreturn_no_args => .{
@@ -2649,6 +2685,7 @@ pub const Type = extern union {
                 .comptime_params = undefined,
                 .return_type = initTag(.noreturn),
                 .cc = .Unspecified,
+                .@"addrspace" = .generic,
                 .is_var_args = false,
                 .is_generic = false,
             },
@@ -2657,6 +2694,7 @@ pub const Type = extern union {
                 .comptime_params = undefined,
                 .return_type = initTag(.void),
                 .cc = .Unspecified,
+                .@"addrspace" = .generic,
                 .is_var_args = false,
                 .is_generic = false,
             },
@@ -2665,6 +2703,7 @@ pub const Type = extern union {
                 .comptime_params = undefined,
                 .return_type = initTag(.noreturn),
                 .cc = .Naked,
+                .@"addrspace" = .generic,
                 .is_var_args = false,
                 .is_generic = false,
             },
@@ -2673,6 +2712,7 @@ pub const Type = extern union {
                 .comptime_params = undefined,
                 .return_type = initTag(.void),
                 .cc = .C,
+                .@"addrspace" = .generic,
                 .is_var_args = false,
                 .is_generic = false,
             },
@@ -3544,6 +3584,7 @@ pub const Type = extern union {
                 comptime_params: [*]bool,
                 return_type: Type,
                 cc: std.builtin.CallingConvention,
+                @"addrspace": std.builtin.AddressSpace,
                 is_var_args: bool,
                 is_generic: bool,
 
@@ -3581,6 +3622,7 @@ pub const Type = extern union {
                 sentinel: ?Value,
                 /// If zero use pointee_type.AbiAlign()
                 @"align": u32,
+                @"addrspace": std.builtin.AddressSpace,
                 bit_offset: u16,
                 host_size: u16,
                 @"allowzero": bool,