commit 1ad905c71e0896295d4781853cd577bbe1b4111a (tree)
parent 2a728f6e5f0c5d12e110313342e714f9f23c4044
Author: Andrew Kelley <andrew@ziglang.org>
Date: Mon, 20 Sep 2021 20:37:04 -0400
Merge pull request #9649 from Snektron/address-space
Address Spaces
Diffstat:
33 files changed, 1173 insertions(+), 220 deletions(-)
diff --git a/doc/docgen.zig b/doc/docgen.zig
@@ -901,6 +901,7 @@ fn tokenizeAndPrintRaw(
switch (token.tag) {
.eof => break,
+ .keyword_addrspace,
.keyword_align,
.keyword_and,
.keyword_asm,
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
@@ -168,6 +168,15 @@ pub const CallingConvention = enum {
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
+pub const AddressSpace = enum {
+ generic,
+ gs,
+ fs,
+ ss,
+};
+
+/// This data structure is used by the Zig language code generation and
+/// therefore must be kept in sync with the compiler implementation.
pub const SourceLocation = struct {
file: [:0]const u8,
fn_name: [:0]const u8,
@@ -226,6 +235,7 @@ pub const TypeInfo = union(enum) {
is_const: bool,
is_volatile: bool,
alignment: comptime_int,
+ address_space: AddressSpace,
child: type,
is_allowzero: bool,
diff --git a/lib/std/mem.zig b/lib/std/mem.zig
@@ -2472,6 +2472,7 @@ fn CopyPtrAttrs(comptime source: type, comptime size: std.builtin.TypeInfo.Point
.is_volatile = info.is_volatile,
.is_allowzero = info.is_allowzero,
.alignment = info.alignment,
+ .address_space = info.address_space,
.child = child,
.sentinel = null,
},
@@ -2960,6 +2961,7 @@ fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: u29) typ
.is_volatile = info.is_volatile,
.is_allowzero = info.is_allowzero,
.alignment = new_alignment,
+ .address_space = info.address_space,
.child = info.child,
.sentinel = null,
},
diff --git a/lib/std/meta.zig b/lib/std/meta.zig
@@ -235,6 +235,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
.is_const = info.is_const,
.is_volatile = info.is_volatile,
.alignment = info.alignment,
+ .address_space = info.address_space,
.child = @Type(.{
.Array = .{
.len = array_info.len,
@@ -254,6 +255,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
.is_const = info.is_const,
.is_volatile = info.is_volatile,
.alignment = info.alignment,
+ .address_space = info.address_space,
.child = info.child,
.is_allowzero = info.is_allowzero,
.sentinel = sentinel_val,
@@ -271,6 +273,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
.is_const = ptr_info.is_const,
.is_volatile = ptr_info.is_volatile,
.alignment = ptr_info.alignment,
+ .address_space = ptr_info.address_space,
.child = ptr_info.child,
.is_allowzero = ptr_info.is_allowzero,
.sentinel = sentinel_val,
diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig
@@ -262,6 +262,9 @@ pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {
token_tags[parse_error.token].symbol(),
});
},
+ .extra_addrspace_qualifier => {
+ return stream.writeAll("extra addrspace qualifier");
+ },
.extra_align_qualifier => {
return stream.writeAll("extra align qualifier");
},
@@ -1021,7 +1024,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
},
.fn_proto_one => {
const extra = tree.extraData(datas[n].lhs, Node.FnProtoOne);
- // linksection, callconv, align can appear in any order, so we
+ // addrspace, linksection, callconv, align can appear in any order, so we
// find the last one here.
var max_node: Node.Index = datas[n].rhs;
var max_start = token_starts[main_tokens[max_node]];
@@ -1034,6 +1037,14 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
max_offset = 1; // for the rparen
}
}
+ if (extra.addrspace_expr != 0) {
+ const start = token_starts[main_tokens[extra.addrspace_expr]];
+ if (start > max_start) {
+ max_node = extra.addrspace_expr;
+ max_start = start;
+ max_offset = 1; // for the rparen
+ }
+ }
if (extra.section_expr != 0) {
const start = token_starts[main_tokens[extra.section_expr]];
if (start > max_start) {
@@ -1055,7 +1066,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
},
.fn_proto => {
const extra = tree.extraData(datas[n].lhs, Node.FnProto);
- // linksection, callconv, align can appear in any order, so we
+ // addrspace, linksection, callconv, align can appear in any order, so we
// find the last one here.
var max_node: Node.Index = datas[n].rhs;
var max_start = token_starts[main_tokens[max_node]];
@@ -1068,6 +1079,14 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
max_offset = 1; // for the rparen
}
}
+ if (extra.addrspace_expr != 0) {
+ const start = token_starts[main_tokens[extra.addrspace_expr]];
+ if (start > max_start) {
+ max_node = extra.addrspace_expr;
+ max_start = start;
+ max_offset = 1; // for the rparen
+ }
+ }
if (extra.section_expr != 0) {
const start = token_starts[main_tokens[extra.section_expr]];
if (start > max_start) {
@@ -1138,6 +1157,7 @@ pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
return tree.fullVarDecl(.{
.type_node = extra.type_node,
.align_node = extra.align_node,
+ .addrspace_node = extra.addrspace_node,
.section_node = extra.section_node,
.init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node],
@@ -1151,6 +1171,7 @@ pub fn localVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
return tree.fullVarDecl(.{
.type_node = extra.type_node,
.align_node = extra.align_node,
+ .addrspace_node = 0,
.section_node = 0,
.init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node],
@@ -1163,6 +1184,7 @@ pub fn simpleVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
return tree.fullVarDecl(.{
.type_node = data.lhs,
.align_node = 0,
+ .addrspace_node = 0,
.section_node = 0,
.init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node],
@@ -1175,6 +1197,7 @@ pub fn alignedVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
return tree.fullVarDecl(.{
.type_node = 0,
.align_node = data.lhs,
+ .addrspace_node = 0,
.section_node = 0,
.init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node],
@@ -1249,6 +1272,7 @@ pub fn fnProtoSimple(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.
.return_type = data.rhs,
.params = params,
.align_expr = 0,
+ .addrspace_expr = 0,
.section_expr = 0,
.callconv_expr = 0,
});
@@ -1265,6 +1289,7 @@ pub fn fnProtoMulti(tree: Tree, node: Node.Index) full.FnProto {
.return_type = data.rhs,
.params = params,
.align_expr = 0,
+ .addrspace_expr = 0,
.section_expr = 0,
.callconv_expr = 0,
});
@@ -1282,6 +1307,7 @@ pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnP
.return_type = data.rhs,
.params = params,
.align_expr = extra.align_expr,
+ .addrspace_expr = extra.addrspace_expr,
.section_expr = extra.section_expr,
.callconv_expr = extra.callconv_expr,
});
@@ -1298,6 +1324,7 @@ pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto {
.return_type = data.rhs,
.params = params,
.align_expr = extra.align_expr,
+ .addrspace_expr = extra.addrspace_expr,
.section_expr = extra.section_expr,
.callconv_expr = extra.callconv_expr,
});
@@ -1453,6 +1480,7 @@ pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType {
return tree.fullPtrType(.{
.main_token = tree.nodes.items(.main_token)[node],
.align_node = data.lhs,
+ .addrspace_node = 0,
.sentinel = 0,
.bit_range_start = 0,
.bit_range_end = 0,
@@ -1466,6 +1494,7 @@ pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType {
return tree.fullPtrType(.{
.main_token = tree.nodes.items(.main_token)[node],
.align_node = 0,
+ .addrspace_node = 0,
.sentinel = data.lhs,
.bit_range_start = 0,
.bit_range_end = 0,
@@ -1480,6 +1509,7 @@ pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType {
return tree.fullPtrType(.{
.main_token = tree.nodes.items(.main_token)[node],
.align_node = extra.align_node,
+ .addrspace_node = extra.addrspace_node,
.sentinel = extra.sentinel,
.bit_range_start = 0,
.bit_range_end = 0,
@@ -1494,6 +1524,7 @@ pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType {
return tree.fullPtrType(.{
.main_token = tree.nodes.items(.main_token)[node],
.align_node = extra.align_node,
+ .addrspace_node = extra.addrspace_node,
.sentinel = extra.sentinel,
.bit_range_start = extra.bit_range_start,
.bit_range_end = extra.bit_range_end,
@@ -2063,6 +2094,7 @@ pub const full = struct {
mut_token: TokenIndex,
type_node: Node.Index,
align_node: Node.Index,
+ addrspace_node: Node.Index,
section_node: Node.Index,
init_node: Node.Index,
};
@@ -2130,6 +2162,7 @@ pub const full = struct {
return_type: Node.Index,
params: []const Node.Index,
align_expr: Node.Index,
+ addrspace_expr: Node.Index,
section_expr: Node.Index,
callconv_expr: Node.Index,
};
@@ -2288,6 +2321,7 @@ pub const full = struct {
pub const Components = struct {
main_token: TokenIndex,
align_node: Node.Index,
+ addrspace_node: Node.Index,
sentinel: Node.Index,
bit_range_start: Node.Index,
bit_range_end: Node.Index,
@@ -2397,6 +2431,7 @@ pub const Error = struct {
expected_var_decl_or_fn,
expected_loop_payload,
expected_container,
+ extra_addrspace_qualifier,
extra_align_qualifier,
extra_allowzero_qualifier,
extra_const_qualifier,
@@ -2723,13 +2758,13 @@ pub const Node = struct {
/// main_token is the `fn` keyword.
/// extern function declarations use this tag.
fn_proto_multi,
- /// `fn(a: b) rhs linksection(e) callconv(f)`. `FnProtoOne[lhs]`.
+ /// `fn(a: b) rhs addrspace(e) linksection(f) callconv(g)`. `FnProtoOne[lhs]`.
/// zero or one parameters.
/// anytype and ... parameters are omitted from the AST tree.
/// main_token is the `fn` keyword.
/// extern function declarations use this tag.
fn_proto_one,
- /// `fn(a: b, c: d) rhs linksection(e) callconv(f)`. `FnProto[lhs]`.
+ /// `fn(a: b, c: d) rhs addrspace(e) linksection(f) callconv(g)`. `FnProto[lhs]`.
/// anytype and ... parameters are omitted from the AST tree.
/// main_token is the `fn` keyword.
/// extern function declarations use this tag.
@@ -2893,11 +2928,13 @@ pub const Node = struct {
pub const PtrType = struct {
sentinel: Index,
align_node: Index,
+ addrspace_node: Index,
};
pub const PtrTypeBitRange = struct {
sentinel: Index,
align_node: Index,
+ addrspace_node: Index,
bit_range_start: Index,
bit_range_end: Index,
};
@@ -2920,8 +2957,13 @@ pub const Node = struct {
};
pub const GlobalVarDecl = struct {
+ /// Populated if there is an explicit type ascription.
type_node: Index,
+ /// Populated if align(A) is present.
align_node: Index,
+ /// Populated if addrspace(A) is present.
+ addrspace_node: Index,
+ /// Populated if linksection(A) is present.
section_node: Index,
};
@@ -2953,6 +2995,8 @@ pub const Node = struct {
param: Index,
/// Populated if align(A) is present.
align_expr: Index,
+ /// Populated if addrspace(A) is present.
+ addrspace_expr: Index,
/// Populated if linksection(A) is present.
section_expr: Index,
/// Populated if callconv(A) is present.
@@ -2964,6 +3008,8 @@ pub const Node = struct {
params_end: Index,
/// Populated if align(A) is present.
align_expr: Index,
+ /// Populated if addrspace(A) is present.
+ addrspace_expr: Index,
/// Populated if linksection(A) is present.
section_expr: Index,
/// Populated if callconv(A) is present.
diff --git a/lib/std/zig/c_translation.zig b/lib/std/zig/c_translation.zig
@@ -325,6 +325,7 @@ pub fn FlexibleArrayType(comptime SelfType: type, ElementType: type) type {
.is_const = ptr.is_const,
.is_volatile = ptr.is_volatile,
.alignment = @alignOf(ElementType),
+ .address_space = .generic,
.child = ElementType,
.is_allowzero = true,
.sentinel = null,
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
@@ -629,7 +629,7 @@ const Parser = struct {
};
}
- /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
+ /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
fn parseFnProto(p: *Parser) !Node.Index {
const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
@@ -639,6 +639,7 @@ const Parser = struct {
_ = p.eatToken(.identifier);
const params = try p.parseParamDeclList();
const align_expr = try p.parseByteAlign();
+ const addrspace_expr = try p.parseAddrSpace();
const section_expr = try p.parseLinkSection();
const callconv_expr = try p.parseCallconv();
_ = p.eatToken(.bang);
@@ -650,7 +651,7 @@ const Parser = struct {
try p.warn(.expected_return_type);
}
- if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
+ if (align_expr == 0 and section_expr == 0 and callconv_expr == 0 and addrspace_expr == 0) {
switch (params) {
.zero_or_one => |param| return p.setNode(fn_proto_index, .{
.tag = .fn_proto_simple,
@@ -683,6 +684,7 @@ const Parser = struct {
.lhs = try p.addExtra(Node.FnProtoOne{
.param = param,
.align_expr = align_expr,
+ .addrspace_expr = addrspace_expr,
.section_expr = section_expr,
.callconv_expr = callconv_expr,
}),
@@ -698,6 +700,7 @@ const Parser = struct {
.params_start = span.start,
.params_end = span.end,
.align_expr = align_expr,
+ .addrspace_expr = addrspace_expr,
.section_expr = section_expr,
.callconv_expr = callconv_expr,
}),
@@ -708,7 +711,7 @@ const Parser = struct {
}
}
- /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
+ /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON
fn parseVarDecl(p: *Parser) !Node.Index {
const mut_token = p.eatToken(.keyword_const) orelse
p.eatToken(.keyword_var) orelse
@@ -717,9 +720,10 @@ const Parser = struct {
_ = try p.expectToken(.identifier);
const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr();
const align_node = try p.parseByteAlign();
+ const addrspace_node = try p.parseAddrSpace();
const section_node = try p.parseLinkSection();
const init_node: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr();
- if (section_node == 0) {
+ if (section_node == 0 and addrspace_node == 0) {
if (align_node == 0) {
return p.addNode(.{
.tag = .simple_var_decl,
@@ -759,6 +763,7 @@ const Parser = struct {
.lhs = try p.addExtra(Node.GlobalVarDecl{
.type_node = type_node,
.align_node = align_node,
+ .addrspace_node = addrspace_node,
.section_node = section_node,
}),
.rhs = init_node,
@@ -1440,8 +1445,8 @@ const Parser = struct {
/// PrefixTypeOp
/// <- QUESTIONMARK
/// / KEYWORD_anyframe MINUSRARROW
- /// / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
- /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
+ /// / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
+ /// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
/// / ArrayTypeStart
/// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET
/// PtrTypeStart
@@ -1474,29 +1479,43 @@ const Parser = struct {
const asterisk = p.nextToken();
const mods = try p.parsePtrModifiers();
const elem_type = try p.expectTypeExpr();
- if (mods.bit_range_start == 0) {
+ if (mods.bit_range_start != 0) {
return p.addNode(.{
- .tag = .ptr_type_aligned,
+ .tag = .ptr_type_bit_range,
.main_token = asterisk,
.data = .{
- .lhs = mods.align_node,
+ .lhs = try p.addExtra(Node.PtrTypeBitRange{
+ .sentinel = 0,
+ .align_node = mods.align_node,
+ .addrspace_node = mods.addrspace_node,
+ .bit_range_start = mods.bit_range_start,
+ .bit_range_end = mods.bit_range_end,
+ }),
.rhs = elem_type,
},
});
- } else {
+ } else if (mods.addrspace_node != 0) {
return p.addNode(.{
- .tag = .ptr_type_bit_range,
+ .tag = .ptr_type,
.main_token = asterisk,
.data = .{
- .lhs = try p.addExtra(Node.PtrTypeBitRange{
+ .lhs = try p.addExtra(Node.PtrType{
.sentinel = 0,
.align_node = mods.align_node,
- .bit_range_start = mods.bit_range_start,
- .bit_range_end = mods.bit_range_end,
+ .addrspace_node = mods.addrspace_node,
}),
.rhs = elem_type,
},
});
+ } else {
+ return p.addNode(.{
+ .tag = .ptr_type_aligned,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = mods.align_node,
+ .rhs = elem_type,
+ },
+ });
}
},
.asterisk_asterisk => {
@@ -1504,29 +1523,43 @@ const Parser = struct {
const mods = try p.parsePtrModifiers();
const elem_type = try p.expectTypeExpr();
const inner: Node.Index = inner: {
- if (mods.bit_range_start == 0) {
+ if (mods.bit_range_start != 0) {
break :inner try p.addNode(.{
- .tag = .ptr_type_aligned,
+ .tag = .ptr_type_bit_range,
.main_token = asterisk,
.data = .{
- .lhs = mods.align_node,
+ .lhs = try p.addExtra(Node.PtrTypeBitRange{
+ .sentinel = 0,
+ .align_node = mods.align_node,
+ .addrspace_node = mods.addrspace_node,
+ .bit_range_start = mods.bit_range_start,
+ .bit_range_end = mods.bit_range_end,
+ }),
.rhs = elem_type,
},
});
- } else {
+ } else if (mods.addrspace_node != 0) {
break :inner try p.addNode(.{
- .tag = .ptr_type_bit_range,
+ .tag = .ptr_type,
.main_token = asterisk,
.data = .{
- .lhs = try p.addExtra(Node.PtrTypeBitRange{
+ .lhs = try p.addExtra(Node.PtrType{
.sentinel = 0,
.align_node = mods.align_node,
- .bit_range_start = mods.bit_range_start,
- .bit_range_end = mods.bit_range_end,
+ .addrspace_node = mods.addrspace_node,
}),
.rhs = elem_type,
},
});
+ } else {
+ break :inner try p.addNode(.{
+ .tag = .ptr_type_aligned,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = mods.align_node,
+ .rhs = elem_type,
+ },
+ });
}
};
return p.addNode(.{
@@ -1560,7 +1593,7 @@ const Parser = struct {
const mods = try p.parsePtrModifiers();
const elem_type = try p.expectTypeExpr();
if (mods.bit_range_start == 0) {
- if (sentinel == 0) {
+ if (sentinel == 0 and mods.addrspace_node == 0) {
return p.addNode(.{
.tag = .ptr_type_aligned,
.main_token = asterisk,
@@ -1569,7 +1602,7 @@ const Parser = struct {
.rhs = elem_type,
},
});
- } else if (mods.align_node == 0) {
+ } else if (mods.align_node == 0 and mods.addrspace_node == 0) {
return p.addNode(.{
.tag = .ptr_type_sentinel,
.main_token = asterisk,
@@ -1586,6 +1619,7 @@ const Parser = struct {
.lhs = try p.addExtra(Node.PtrType{
.sentinel = sentinel,
.align_node = mods.align_node,
+ .addrspace_node = mods.addrspace_node,
}),
.rhs = elem_type,
},
@@ -1599,6 +1633,7 @@ const Parser = struct {
.lhs = try p.addExtra(Node.PtrTypeBitRange{
.sentinel = sentinel,
.align_node = mods.align_node,
+ .addrspace_node = mods.addrspace_node,
.bit_range_start = mods.bit_range_start,
.bit_range_end = mods.bit_range_end,
}),
@@ -1624,7 +1659,7 @@ const Parser = struct {
.token = p.nodes.items(.main_token)[mods.bit_range_start],
});
}
- if (sentinel == 0) {
+ if (sentinel == 0 and mods.addrspace_node == 0) {
return p.addNode(.{
.tag = .ptr_type_aligned,
.main_token = lbracket,
@@ -1633,7 +1668,7 @@ const Parser = struct {
.rhs = elem_type,
},
});
- } else if (mods.align_node == 0) {
+ } else if (mods.align_node == 0 and mods.addrspace_node == 0) {
return p.addNode(.{
.tag = .ptr_type_sentinel,
.main_token = lbracket,
@@ -1650,6 +1685,7 @@ const Parser = struct {
.lhs = try p.addExtra(Node.PtrType{
.sentinel = sentinel,
.align_node = mods.align_node,
+ .addrspace_node = mods.addrspace_node,
}),
.rhs = elem_type,
},
@@ -1661,6 +1697,7 @@ const Parser = struct {
.keyword_const,
.keyword_volatile,
.keyword_allowzero,
+ .keyword_addrspace,
=> return p.fail(.ptr_mod_on_array_child_type),
else => {},
}
@@ -2879,6 +2916,15 @@ const Parser = struct {
return expr_node;
}
+ /// AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN
+ fn parseAddrSpace(p: *Parser) !Node.Index {
+ _ = p.eatToken(.keyword_addrspace) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const expr_node = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ return expr_node;
+ }
+
/// ParamDecl
/// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
/// / DOT3
@@ -3011,6 +3057,7 @@ const Parser = struct {
const PtrModifiers = struct {
align_node: Node.Index,
+ addrspace_node: Node.Index,
bit_range_start: Node.Index,
bit_range_end: Node.Index,
};
@@ -3018,12 +3065,14 @@ const Parser = struct {
fn parsePtrModifiers(p: *Parser) !PtrModifiers {
var result: PtrModifiers = .{
.align_node = 0,
+ .addrspace_node = 0,
.bit_range_start = 0,
.bit_range_end = 0,
};
var saw_const = false;
var saw_volatile = false;
var saw_allowzero = false;
+ var saw_addrspace = false;
while (true) {
switch (p.token_tags[p.tok_i]) {
.keyword_align => {
@@ -3063,6 +3112,12 @@ const Parser = struct {
p.tok_i += 1;
saw_allowzero = true;
},
+ .keyword_addrspace => {
+ if (saw_addrspace) {
+ try p.warn(.extra_addrspace_qualifier);
+ }
+ result.addrspace_node = try p.parseAddrSpace();
+ },
else => return result,
}
}
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
@@ -404,6 +404,10 @@ test "zig fmt: trailing comma in fn parameter list" {
\\pub fn f(
\\ a: i32,
\\ b: i32,
+ \\) addrspace(.generic) i32 {}
+ \\pub fn f(
+ \\ a: i32,
+ \\ b: i32,
\\) linksection(".text") i32 {}
\\pub fn f(
\\ a: i32,
@@ -553,8 +557,8 @@ test "zig fmt: sentinel-terminated slice type" {
test "zig fmt: pointer-to-one with modifiers" {
try testCanonical(
\\const x: *u32 = undefined;
- \\const y: *allowzero align(8) const volatile u32 = undefined;
- \\const z: *allowzero align(8:4:2) const volatile u32 = undefined;
+ \\const y: *allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
+ \\const z: *allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
@@ -562,8 +566,8 @@ test "zig fmt: pointer-to-one with modifiers" {
test "zig fmt: pointer-to-many with modifiers" {
try testCanonical(
\\const x: [*]u32 = undefined;
- \\const y: [*]allowzero align(8) const volatile u32 = undefined;
- \\const z: [*]allowzero align(8:4:2) const volatile u32 = undefined;
+ \\const y: [*]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
+ \\const z: [*]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
@@ -571,8 +575,8 @@ test "zig fmt: pointer-to-many with modifiers" {
test "zig fmt: sentinel pointer with modifiers" {
try testCanonical(
\\const x: [*:42]u32 = undefined;
- \\const y: [*:42]allowzero align(8) const volatile u32 = undefined;
- \\const y: [*:42]allowzero align(8:4:2) const volatile u32 = undefined;
+ \\const y: [*:42]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
+ \\const y: [*:42]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
@@ -580,8 +584,8 @@ test "zig fmt: sentinel pointer with modifiers" {
test "zig fmt: c pointer with modifiers" {
try testCanonical(
\\const x: [*c]u32 = undefined;
- \\const y: [*c]allowzero align(8) const volatile u32 = undefined;
- \\const z: [*c]allowzero align(8:4:2) const volatile u32 = undefined;
+ \\const y: [*c]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
+ \\const z: [*c]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
@@ -589,7 +593,7 @@ test "zig fmt: c pointer with modifiers" {
test "zig fmt: slice with modifiers" {
try testCanonical(
\\const x: []u32 = undefined;
- \\const y: []allowzero align(8) const volatile u32 = undefined;
+ \\const y: []allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
@@ -597,7 +601,7 @@ test "zig fmt: slice with modifiers" {
test "zig fmt: sentinel slice with modifiers" {
try testCanonical(
\\const x: [:42]u32 = undefined;
- \\const y: [:42]allowzero align(8) const volatile u32 = undefined;
+ \\const y: [:42]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
@@ -1129,6 +1133,16 @@ test "zig fmt: linksection" {
);
}
+test "zig fmt: addrspace" {
+ try testCanonical(
+ \\export var python_length: u64 align(1) addrspace(.generic);
+ \\export var python_color: Color addrspace(.generic) = .green;
+ \\export var python_legs: u0 align(8) addrspace(.generic) linksection(".python") = 0;
+ \\export fn python_hiss() align(8) addrspace(.generic) linksection(".python") void;
+ \\
+ );
+}
+
test "zig fmt: correctly space struct fields with doc comments" {
try testTransform(
\\pub const S = struct {
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
@@ -797,6 +797,14 @@ fn renderPtrType(
}
}
+ if (ptr_type.ast.addrspace_node != 0) {
+ const addrspace_first = tree.firstToken(ptr_type.ast.addrspace_node);
+ try renderToken(ais, tree, addrspace_first - 2, .none); // addrspace
+ try renderToken(ais, tree, addrspace_first - 1, .none); // lparen
+ try renderExpression(gpa, ais, tree, ptr_type.ast.addrspace_node, .none);
+ try renderToken(ais, tree, tree.lastToken(ptr_type.ast.addrspace_node) + 1, .space); // rparen
+ }
+
if (ptr_type.const_token) |const_token| {
try renderToken(ais, tree, const_token, .space);
}
@@ -921,6 +929,7 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe
const name_space = if (var_decl.ast.type_node == 0 and
(var_decl.ast.align_node != 0 or
+ var_decl.ast.addrspace_node != 0 or
var_decl.ast.section_node != 0 or
var_decl.ast.init_node != 0))
Space.space
@@ -930,8 +939,8 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe
if (var_decl.ast.type_node != 0) {
try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // :
- if (var_decl.ast.align_node != 0 or var_decl.ast.section_node != 0 or
- var_decl.ast.init_node != 0)
+ if (var_decl.ast.align_node != 0 or var_decl.ast.addrspace_node != 0 or
+ var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0)
{
try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space);
} else {
@@ -948,6 +957,23 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe
try renderToken(ais, tree, align_kw, Space.none); // align
try renderToken(ais, tree, lparen, Space.none); // (
try renderExpression(gpa, ais, tree, var_decl.ast.align_node, Space.none);
+ if (var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or
+ var_decl.ast.init_node != 0)
+ {
+ try renderToken(ais, tree, rparen, .space); // )
+ } else {
+ try renderToken(ais, tree, rparen, .none); // )
+ return renderToken(ais, tree, rparen + 1, Space.newline); // ;
+ }
+ }
+
+ if (var_decl.ast.addrspace_node != 0) {
+ const lparen = tree.firstToken(var_decl.ast.addrspace_node) - 1;
+ const addrspace_kw = lparen - 1;
+ const rparen = tree.lastToken(var_decl.ast.addrspace_node) + 1;
+ try renderToken(ais, tree, addrspace_kw, Space.none); // addrspace
+ try renderToken(ais, tree, lparen, Space.none); // (
+ try renderExpression(gpa, ais, tree, var_decl.ast.addrspace_node, Space.none);
if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) {
try renderToken(ais, tree, rparen, .space); // )
} else {
@@ -1267,6 +1293,14 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro
smallest_start = start;
}
}
+ if (fn_proto.ast.addrspace_expr != 0) {
+ const tok = tree.firstToken(fn_proto.ast.addrspace_expr) - 3;
+ const start = token_starts[tok];
+ if (start < smallest_start) {
+ rparen = tok;
+ smallest_start = start;
+ }
+ }
if (fn_proto.ast.section_expr != 0) {
const tok = tree.firstToken(fn_proto.ast.section_expr) - 3;
const start = token_starts[tok];
@@ -1407,6 +1441,16 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro
try renderToken(ais, tree, align_rparen, .space); // )
}
+ if (fn_proto.ast.addrspace_expr != 0) {
+ const align_lparen = tree.firstToken(fn_proto.ast.addrspace_expr) - 1;
+ const align_rparen = tree.lastToken(fn_proto.ast.addrspace_expr) + 1;
+
+ try renderToken(ais, tree, align_lparen - 1, .none); // addrspace
+ try renderToken(ais, tree, align_lparen, .none); // (
+ try renderExpression(gpa, ais, tree, fn_proto.ast.addrspace_expr, .none);
+ try renderToken(ais, tree, align_rparen, .space); // )
+ }
+
if (fn_proto.ast.section_expr != 0) {
const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;
const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;
diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig
@@ -11,6 +11,7 @@ pub const Token = struct {
};
pub const keywords = std.ComptimeStringMap(Tag, .{
+ .{ "addrspace", .keyword_addrspace },
.{ "align", .keyword_align },
.{ "allowzero", .keyword_allowzero },
.{ "and", .keyword_and },
@@ -132,6 +133,7 @@ pub const Token = struct {
float_literal,
doc_comment,
container_doc_comment,
+ keyword_addrspace,
keyword_align,
keyword_allowzero,
keyword_and,
@@ -251,6 +253,7 @@ pub const Token = struct {
.angle_bracket_angle_bracket_right => ">>",
.angle_bracket_angle_bracket_right_equal => ">>=",
.tilde => "~",
+ .keyword_addrspace => "addrspace",
.keyword_align => "align",
.keyword_allowzero => "allowzero",
.keyword_and => "and",
diff --git a/src/AstGen.zig b/src/AstGen.zig
@@ -1116,6 +1116,11 @@ fn fnProtoExpr(
const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
break :inst try expr(gz, scope, align_rl, fn_proto.ast.align_expr);
};
+
+ if (fn_proto.ast.addrspace_expr != 0) {
+ return astgen.failNode(fn_proto.ast.addrspace_expr, "addrspace not allowed on function prototypes", .{});
+ }
+
if (fn_proto.ast.section_expr != 0) {
return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{});
}
@@ -2371,6 +2376,7 @@ fn varDecl(
const gpa = astgen.gpa;
const tree = astgen.tree;
const token_tags = tree.tokens.items(.tag);
+ const main_tokens = tree.nodes.items(.main_token);
const name_token = var_decl.ast.mut_token + 1;
const ident_name_raw = tree.tokenSlice(name_token);
@@ -2385,6 +2391,14 @@ fn varDecl(
return astgen.failNode(node, "variables must be initialized", .{});
}
+ if (var_decl.ast.addrspace_node != 0) {
+ return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
+ }
+
+ if (var_decl.ast.section_node != 0) {
+ return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
+ }
+
const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
try expr(gz, scope, align_rl, var_decl.ast.align_node)
else
@@ -2714,6 +2728,7 @@ fn ptrType(
const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
const simple = ptr_info.ast.align_node == 0 and
+ ptr_info.ast.addrspace_node == 0 and
ptr_info.ast.sentinel == 0 and
ptr_info.ast.bit_range_start == 0;
@@ -2732,6 +2747,7 @@ fn ptrType(
var sentinel_ref: Zir.Inst.Ref = .none;
var align_ref: Zir.Inst.Ref = .none;
+ var addrspace_ref: Zir.Inst.Ref = .none;
var bit_start_ref: Zir.Inst.Ref = .none;
var bit_end_ref: Zir.Inst.Ref = .none;
var trailing_count: u32 = 0;
@@ -2744,6 +2760,10 @@ fn ptrType(
align_ref = try expr(gz, scope, align_rl, ptr_info.ast.align_node);
trailing_count += 1;
}
+ if (ptr_info.ast.addrspace_node != 0) {
+ addrspace_ref = try expr(gz, scope, .{ .ty = .address_space_type }, ptr_info.ast.addrspace_node);
+ trailing_count += 1;
+ }
if (ptr_info.ast.bit_range_start != 0) {
assert(ptr_info.ast.bit_range_end != 0);
bit_start_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_start);
@@ -2764,6 +2784,9 @@ fn ptrType(
if (align_ref != .none) {
gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref));
}
+ if (addrspace_ref != .none) {
+ gz.astgen.extra.appendAssumeCapacity(@enumToInt(addrspace_ref));
+ }
if (bit_start_ref != .none) {
gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
@@ -2779,6 +2802,7 @@ fn ptrType(
.is_volatile = ptr_info.volatile_token != null,
.has_sentinel = sentinel_ref != .none,
.has_align = align_ref != .none,
+ .has_addrspace = addrspace_ref != .none,
.has_bit_range = bit_start_ref != .none,
},
.size = ptr_info.size,
@@ -2847,7 +2871,7 @@ const WipDecls = struct {
is_pub: bool,
is_export: bool,
has_align: bool,
- has_section: bool,
+ has_section_or_addrspace: bool,
) Allocator.Error!void {
if (wip_decls.decl_index % fields_per_u32 == 0 and wip_decls.decl_index != 0) {
try wip_decls.bit_bag.append(gpa, wip_decls.cur_bit_bag);
@@ -2857,7 +2881,7 @@ const WipDecls = struct {
(@as(u32, @boolToInt(is_pub)) << 28) |
(@as(u32, @boolToInt(is_export)) << 29) |
(@as(u32, @boolToInt(has_align)) << 30) |
- (@as(u32, @boolToInt(has_section)) << 31);
+ (@as(u32, @boolToInt(has_section_or_addrspace)) << 31);
wip_decls.decl_index += 1;
}
@@ -2922,7 +2946,8 @@ fn fnDecl(
const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
break :blk token_tags[maybe_inline_token] == .keyword_inline;
};
- try wip_decls.next(gpa, is_pub, is_export, fn_proto.ast.align_expr != 0, fn_proto.ast.section_expr != 0);
+ const has_section_or_addrspace = fn_proto.ast.section_expr != 0 or fn_proto.ast.addrspace_expr != 0;
+ try wip_decls.next(gpa, is_pub, is_export, fn_proto.ast.align_expr != 0, has_section_or_addrspace);
var params_scope = &fn_gz.base;
const is_var_args = is_var_args: {
@@ -3011,6 +3036,9 @@ fn fnDecl(
const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
break :inst try expr(&decl_gz, params_scope, align_rl, fn_proto.ast.align_expr);
};
+ const addrspace_inst: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: {
+ break :inst try expr(&decl_gz, params_scope, .{ .ty = .address_space_type }, fn_proto.ast.addrspace_expr);
+ };
const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
break :inst try comptimeExpr(&decl_gz, params_scope, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr);
};
@@ -3112,7 +3140,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);
@@ -3127,8 +3155,10 @@ fn fnDecl(
if (align_inst != .none) {
wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));
}
- if (section_inst != .none) {
+
+ if (has_section_or_addrspace) {
wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst));
+ wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst));
}
}
@@ -3175,10 +3205,14 @@ fn globalVarDecl(
const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: {
break :inst try expr(&block_scope, &block_scope.base, align_rl, var_decl.ast.align_node);
};
+ const addrspace_inst: Zir.Inst.Ref = if (var_decl.ast.addrspace_node == 0) .none else inst: {
+ break :inst try expr(&block_scope, &block_scope.base, .{ .ty = .address_space_type }, var_decl.ast.addrspace_node);
+ };
const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: {
break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .ty = .const_slice_u8_type }, var_decl.ast.section_node);
};
- try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, section_inst != .none);
+ const has_section_or_addrspace = section_inst != .none or addrspace_inst != .none;
+ try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, has_section_or_addrspace);
const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
if (!is_mutable) {
@@ -3256,7 +3290,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);
@@ -3271,8 +3305,9 @@ fn globalVarDecl(
if (align_inst != .none) {
wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));
}
- if (section_inst != .none) {
+ if (has_section_or_addrspace) {
wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst));
+ wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst));
}
}
diff --git a/src/Module.zig b/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 `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,24 @@ 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_ctx: Sema.AddressSpaceContext = switch (decl_tv.val.tag()) {
+ .function, .extern_fn => .function,
+ .variable => .variable,
+ else => .constant,
+ };
+
+ break :blk switch (decl.zirAddrspaceRef()) {
+ .none => switch (addrspace_ctx) {
+ .function => target_util.defaultAddressSpace(sema.mod.getTarget(), .function),
+ .variable => target_util.defaultAddressSpace(sema.mod.getTarget(), .global_mutable),
+ .constant => target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant),
+ else => unreachable,
+ },
+ else => |addrspace_ref| try sema.analyzeAddrspace(&block_scope, src, addrspace_ref, addrspace_ctx),
+ };
+ };
+
// 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 +3286,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 +3348,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 +3556,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 +3583,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 +3669,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 +3686,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 +4058,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 +4083,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,
@@ -4185,6 +4216,9 @@ pub fn createAnonymousDeclFromDeclNamed(
new_decl.src_line = owner_decl.src_line;
new_decl.ty = typed_value.ty;
new_decl.val = typed_value.val;
+ new_decl.align_val = Value.initTag(.null_value);
+ new_decl.linksection_val = Value.initTag(.null_value);
+ new_decl.@"addrspace" = .generic; // default global addrspace
new_decl.has_tv = true;
new_decl.analysis = .complete;
new_decl.generation = mod.generation;
@@ -4330,10 +4364,59 @@ pub fn simplePtrType(
elem_ty: Type,
mutable: bool,
size: std.builtin.TypeInfo.Pointer.Size,
+ @"addrspace": std.builtin.AddressSpace,
) Allocator.Error!Type {
+ return ptrType(
+ arena,
+ elem_ty,
+ null,
+ 0,
+ @"addrspace",
+ 0,
+ 0,
+ mutable,
+ false,
+ false,
+ size,
+ );
+}
+
+pub fn ptrType(
+ arena: *Allocator,
+ elem_ty: Type,
+ sentinel: ?Value,
+ @"align": u32,
+ @"addrspace": std.builtin.AddressSpace,
+ bit_offset: u16,
+ host_size: u16,
+ mutable: bool,
+ @"allowzero": bool,
+ @"volatile": bool,
+ size: std.builtin.TypeInfo.Pointer.Size,
+) Allocator.Error!Type {
+ assert(host_size == 0 or bit_offset < host_size * 8);
+
+ if (sentinel != null or @"align" != 0 or @"addrspace" != .generic or
+ bit_offset != 0 or host_size != 0 or @"allowzero" or @"volatile")
+ {
+ return Type.Tag.pointer.create(arena, .{
+ .pointee_type = elem_ty,
+ .sentinel = sentinel,
+ .@"align" = @"align",
+ .@"addrspace" = @"addrspace",
+ .bit_offset = bit_offset,
+ .host_size = host_size,
+ .@"allowzero" = @"allowzero",
+ .mutable = mutable,
+ .@"volatile" = @"volatile",
+ .size = size,
+ });
+ }
+
if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
return Type.initTag(.const_slice_u8);
}
+
// TODO stage1 type inference bug
const T = Type.Tag;
@@ -4352,34 +4435,6 @@ pub fn simplePtrType(
return Type.initPayload(&type_payload.base);
}
-pub fn ptrType(
- arena: *Allocator,
- elem_ty: Type,
- sentinel: ?Value,
- @"align": u32,
- bit_offset: u16,
- host_size: u16,
- mutable: bool,
- @"allowzero": bool,
- @"volatile": bool,
- size: std.builtin.TypeInfo.Pointer.Size,
-) Allocator.Error!Type {
- assert(host_size == 0 or bit_offset < host_size * 8);
-
- // TODO check if type can be represented by simplePtrType
- return Type.Tag.pointer.create(arena, .{
- .pointee_type = elem_ty,
- .sentinel = sentinel,
- .@"align" = @"align",
- .bit_offset = bit_offset,
- .host_size = host_size,
- .@"allowzero" = @"allowzero",
- .mutable = mutable,
- .@"volatile" = @"volatile",
- .size = size,
- });
-}
-
pub fn optionalType(arena: *Allocator, child_type: Type) Allocator.Error!Type {
switch (child_type.tag()) {
.single_const_pointer => return Type.Tag.optional_single_const_pointer.create(
@@ -4709,7 +4764,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file;
const builtin_namespace = builtin_file.root_decl.?.namespace;
const decl = builtin_namespace.decls.get("test_functions").?;
- var buf: Type.Payload.ElemType = undefined;
+ var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType();
const array_decl = d: {
diff --git a/src/Sema.zig b/src/Sema.zig
@@ -1373,7 +1373,13 @@ fn zirRetPtr(
return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty);
}
- const ptr_type = try Module.simplePtrType(sema.arena, sema.fn_ret_ty, true, .One);
+ const ptr_type = try Module.simplePtrType(
+ sema.arena,
+ sema.fn_ret_ty,
+ true,
+ .One,
+ target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ );
return block.addTy(.alloc, ptr_type);
}
@@ -1521,7 +1527,13 @@ fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError
const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
const var_decl_src = inst_data.src();
const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
- const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);
+ const ptr_type = try Module.simplePtrType(
+ sema.arena,
+ var_type,
+ true,
+ .One,
+ target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ );
try sema.requireRuntimeBlock(block, var_decl_src);
return block.addTy(.alloc, ptr_type);
}
@@ -1538,7 +1550,13 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
return sema.analyzeComptimeAlloc(block, var_type);
}
try sema.validateVarType(block, ty_src, var_type);
- const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);
+ const ptr_type = try Module.simplePtrType(
+ sema.arena,
+ var_type,
+ true,
+ .One,
+ target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ );
try sema.requireRuntimeBlock(block, var_decl_src);
return block.addTy(.alloc, ptr_type);
}
@@ -1598,7 +1616,13 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
try sema.mod.declareDeclDependency(sema.owner_decl, decl);
const final_elem_ty = try decl.ty.copy(sema.arena);
- const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One);
+ const final_ptr_ty = try Module.simplePtrType(
+ sema.arena,
+ final_elem_ty,
+ true,
+ .One,
+ target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ );
const final_ptr_ty_inst = try sema.addType(final_ptr_ty);
sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst;
@@ -1620,7 +1644,13 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
try sema.validateVarType(block, ty_src, final_elem_ty);
}
// Change it to a normal alloc.
- const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One);
+ const final_ptr_ty = try Module.simplePtrType(
+ sema.arena,
+ final_elem_ty,
+ true,
+ .One,
+ target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ );
sema.air_instructions.set(ptr_inst, .{
.tag = .alloc,
.data = .{ .ty = final_ptr_ty },
@@ -1774,7 +1804,14 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
}
const ptr = sema.resolveInst(bin_inst.lhs);
const value = sema.resolveInst(bin_inst.rhs);
- const ptr_ty = try Module.simplePtrType(sema.arena, sema.typeOf(value), true, .One);
+ const ptr_ty = try Module.simplePtrType(
+ sema.arena,
+ sema.typeOf(value),
+ true,
+ .One,
+ // TODO figure out which address space is appropriate here
+ target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ );
// TODO detect when this store should be done at compile-time. For example,
// if expressions should force it when the condition is compile-time known.
const src: LazySrcLoc = .unneeded;
@@ -1821,7 +1858,14 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
// for the inferred allocation.
try inferred_alloc.data.stored_inst_list.append(sema.arena, operand);
// Create a runtime bitcast instruction with exactly the type the pointer wants.
- const ptr_ty = try Module.simplePtrType(sema.arena, operand_ty, true, .One);
+ const ptr_ty = try Module.simplePtrType(
+ sema.arena,
+ operand_ty,
+ true,
+ .One,
+ // TODO figure out which address space is appropriate here
+ target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ );
const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr);
return sema.storePtr(block, src, bitcasted_ptr, operand);
}
@@ -3004,7 +3048,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;
@@ -3658,7 +3702,13 @@ fn zirOptionalPayloadPtr(
}
const child_type = try opt_type.optionalChildAlloc(sema.arena);
- const child_pointer = try Module.simplePtrType(sema.arena, child_type, !optional_ptr_ty.isConstPtr(), .One);
+ const child_pointer = try Module.simplePtrType(
+ sema.arena,
+ child_type,
+ !optional_ptr_ty.isConstPtr(),
+ .One,
+ optional_ptr_ty.ptrAddressSpace(),
+ );
if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
if (try pointer_val.pointerDeref(sema.arena)) |val| {
@@ -3773,7 +3823,13 @@ fn zirErrUnionPayloadPtr(
return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand_ty.elemType()});
const payload_ty = operand_ty.elemType().errorUnionPayload();
- const operand_pointer_ty = try Module.simplePtrType(sema.arena, payload_ty, !operand_ty.isConstPtr(), .One);
+ const operand_pointer_ty = try Module.simplePtrType(
+ sema.arena,
+ payload_ty,
+ !operand_ty.isConstPtr(),
+ .One,
+ operand_ty.ptrAddressSpace(),
+ );
if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
if (try pointer_val.pointerDeref(sema.arena)) |val| {
@@ -6879,6 +6935,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp
elem_type,
null,
0,
+ .generic,
0,
0,
inst_data.is_mutable,
@@ -6911,6 +6968,12 @@ 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;
+ break :blk try sema.analyzeAddrspace(block, .unneeded, ref, .pointer);
+ } 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;
@@ -6933,6 +6996,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,
@@ -8339,7 +8403,13 @@ fn panicWithMsg(
const panic_fn = try sema.getBuiltin(block, src, "panic");
const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
- const ptr_stack_trace_ty = try Module.simplePtrType(arena, stack_trace_ty, true, .One);
+ const ptr_stack_trace_ty = try Module.simplePtrType(
+ arena,
+ stack_trace_ty,
+ true,
+ .One,
+ target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant), // TODO might need a place that is more dynamic
+ );
const null_stack_trace = try sema.addConstant(
try Module.optionalType(arena, ptr_stack_trace_ty),
Value.initTag(.null_value),
@@ -8423,7 +8493,7 @@ fn fieldVal(
.Pointer => switch (object_ty.ptrSize()) {
.Slice => {
if (mem.eql(u8, field_name, "ptr")) {
- const buf = try arena.create(Type.Payload.ElemType);
+ const buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
const result_ty = object_ty.slicePtrFieldType(buf);
if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
if (val.isUndef()) return sema.addConstUndef(result_ty);
@@ -8457,21 +8527,32 @@ fn fieldVal(
}
},
.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 },
- );
- }
+ const ptr_child = object_ty.elemType();
+ switch (ptr_child.zigTypeTag()) {
+ .Array => {
+ if (mem.eql(u8, field_name, "len")) {
+ return sema.addConstant(
+ Type.initTag(.comptime_int),
+ try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
+ );
+ } else {
+ return mod.fail(
+ &block.base,
+ field_name_src,
+ "no member named '{s}' in '{}'",
+ .{ field_name, object_ty },
+ );
+ }
+ },
+ .Struct => {
+ const struct_ptr_deref = try sema.analyzeLoad(block, src, object, object_src);
+ return sema.unionFieldVal(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child);
+ },
+ .Union => {
+ const union_ptr_deref = try sema.analyzeLoad(block, src, object, object_src);
+ return sema.unionFieldVal(block, src, union_ptr_deref, field_name, field_name_src, ptr_child);
+ },
+ else => {},
}
},
.Many, .C => {},
@@ -8595,9 +8676,8 @@ fn fieldPtr(
);
}
},
- .Pointer => {
- const ptr_child = object_ty.elemType();
- if (ptr_child.isSlice()) {
+ .Pointer => switch (object_ty.ptrSize()) {
+ .Slice => {
// Here for the ptr and len fields what we need to do is the situation
// when a temporary has its address taken, e.g. `&a[c..d].len`.
// This value may be known at compile-time or runtime. In the former
@@ -8627,26 +8707,39 @@ fn fieldPtr(
.{ field_name, object_ty },
);
}
- } else switch (ptr_child.zigTypeTag()) {
- .Array => {
- if (mem.eql(u8, field_name, "len")) {
- var anon_decl = try block.startAnonDecl();
- defer anon_decl.deinit();
- return sema.analyzeDeclRef(try anon_decl.finish(
- Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
- ));
- } else {
- return mod.fail(
- &block.base,
- field_name_src,
- "no member named '{s}' in '{}'",
- .{ field_name, object_ty },
- );
- }
- },
- else => {},
- }
+ },
+ .One => {
+ const ptr_child = object_ty.elemType();
+ switch (ptr_child.zigTypeTag()) {
+ .Array => {
+ if (mem.eql(u8, field_name, "len")) {
+ var anon_decl = try block.startAnonDecl();
+ defer anon_decl.deinit();
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ Type.initTag(.comptime_int),
+ try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
+ ));
+ } else {
+ return mod.fail(
+ &block.base,
+ field_name_src,
+ "no member named '{s}' in '{}'",
+ .{ field_name, object_ty },
+ );
+ }
+ },
+ .Struct => {
+ const struct_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
+ return sema.structFieldPtr(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child);
+ },
+ .Union => {
+ const union_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
+ return sema.unionFieldPtr(block, src, union_ptr_deref, field_name, field_name_src, ptr_child);
+ },
+ else => {},
+ }
+ },
+ .Many, .C => {},
},
.Type => {
_ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);
@@ -8788,13 +8881,20 @@ fn structFieldPtr(
const arena = sema.arena;
assert(unresolved_struct_ty.zigTypeTag() == .Struct);
+ const struct_ptr_ty = sema.typeOf(struct_ptr);
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];
- const ptr_field_ty = try Module.simplePtrType(arena, field.ty, true, .One);
+ const ptr_field_ty = try Module.simplePtrType(
+ arena,
+ field.ty,
+ struct_ptr_ty.ptrIsMutable(),
+ .One,
+ struct_ptr_ty.ptrAddressSpace(),
+ );
if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
return sema.addConstant(
@@ -8885,6 +8985,7 @@ fn unionFieldPtr(
const arena = sema.arena;
assert(unresolved_union_ty.zigTypeTag() == .Union);
+ const union_ptr_ty = sema.typeOf(union_ptr);
const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
@@ -8892,7 +8993,13 @@ fn unionFieldPtr(
return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
const field = union_obj.fields.values()[field_index];
- const ptr_field_ty = try Module.simplePtrType(arena, field.ty, true, .One);
+ const ptr_field_ty = try Module.simplePtrType(
+ arena,
+ field.ty,
+ union_ptr_ty.ptrIsMutable(),
+ .One,
+ union_ptr_ty.ptrAddressSpace(),
+ );
if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {
// TODO detect inactive union field and emit compile error
@@ -9068,10 +9175,13 @@ fn elemPtrArray(
) CompileError!Air.Inst.Ref {
const array_ptr_ty = sema.typeOf(array_ptr);
const pointee_type = array_ptr_ty.elemType().elemType();
- const result_ty = if (array_ptr_ty.ptrIsMutable())
- try Type.Tag.single_mut_pointer.create(sema.arena, pointee_type)
- else
- try Type.Tag.single_const_pointer.create(sema.arena, pointee_type);
+ const result_ty = try Module.simplePtrType(
+ sema.arena,
+ pointee_type,
+ array_ptr_ty.ptrIsMutable(),
+ .One,
+ array_ptr_ty.ptrAddressSpace(),
+ );
if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {
@@ -9162,6 +9272,7 @@ fn coerce(
const dest_is_mut = !dest_type.isConstPtr();
if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr;
if (inst_ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
+ if (inst_ty.ptrAddressSpace() != dest_type.ptrAddressSpace()) break :src_array_ptr;
const dst_elem_type = dest_type.elemType();
switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut)) {
@@ -9297,6 +9408,10 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool) InM
return child;
}
+ if (dest_info.@"addrspace" != src_info.@"addrspace") {
+ return .no_match;
+ }
+
const ok_sent = dest_info.sentinel == null or src_info.size == .C or
(src_info.sentinel != null and
dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type));
@@ -9590,11 +9705,11 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
const decl_tv = try decl.typedValue();
if (decl_tv.val.castTag(.variable)) |payload| {
const variable = payload.data;
- const ty = try Module.simplePtrType(sema.arena, decl_tv.ty, variable.is_mutable, .One);
+ const ty = try Module.simplePtrType(sema.arena, decl_tv.ty, variable.is_mutable, .One, decl.@"addrspace");
return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl));
}
return sema.addConstant(
- try Module.simplePtrType(sema.arena, decl_tv.ty, false, .One),
+ try Module.simplePtrType(sema.arena, decl_tv.ty, false, .One, decl.@"addrspace"),
try Value.Tag.decl_ref.create(sema.arena, decl),
);
}
@@ -9617,8 +9732,9 @@ fn analyzeRef(
}
try sema.requireRuntimeBlock(block, src);
- const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
- const mut_ptr_type = try Module.simplePtrType(sema.arena, operand_ty, true, .One);
+ const address_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local);
+ const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One, address_space);
+ const mut_ptr_type = try Module.simplePtrType(sema.arena, operand_ty, true, .One, address_space);
const alloc = try block.addTy(.alloc, mut_ptr_type);
try sema.storePtr(block, src, alloc, operand);
@@ -9779,6 +9895,7 @@ fn analyzeSlice(
return_elem_type,
if (end_opt == .none) slice_sentinel else null,
0, // TODO alignment
+ if (ptr_child.zigTypeTag() == .Pointer) ptr_child.ptrAddressSpace() else .generic,
0,
0,
!ptr_child.isConstPtr(),
@@ -10286,6 +10403,7 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type
.atomic_order => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrder"),
.atomic_rmw_op => return sema.resolveBuiltinTypeFields(block, src, "AtomicRmwOp"),
.calling_convention => return sema.resolveBuiltinTypeFields(block, src, "CallingConvention"),
+ .address_space => return sema.resolveBuiltinTypeFields(block, src, "AddressSpace"),
.float_mode => return sema.resolveBuiltinTypeFields(block, src, "FloatMode"),
.reduce_op => return sema.resolveBuiltinTypeFields(block, src, "ReduceOp"),
.call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"),
@@ -10680,6 +10798,7 @@ fn typeHasOnePossibleValue(
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -10865,6 +10984,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
.atomic_order => return .atomic_order_type,
.atomic_rmw_op => return .atomic_rmw_op_type,
.calling_convention => return .calling_convention_type,
+ .address_space => return .address_space_type,
.float_mode => return .float_mode_type,
.reduce_op => return .reduce_op_type,
.call_options => return .call_options_type,
@@ -10960,7 +11080,13 @@ fn analyzeComptimeAlloc(
block: *Scope.Block,
var_type: Type,
) CompileError!Air.Inst.Ref {
- const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);
+ const ptr_type = try Module.simplePtrType(
+ sema.arena,
+ var_type,
+ true,
+ .One,
+ target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant),
+ );
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
@@ -10976,3 +11102,58 @@ fn analyzeComptimeAlloc(
.decl = decl,
}));
}
+
+/// The places where a user can specify an address space attribute
+pub const AddressSpaceContext = enum {
+ /// A function is specificed to be placed in a certain address space.
+ function,
+
+ /// A (global) variable is specified to be placed in a certain address space.
+ /// In contrast to .constant, these values (and thus the address space they will be
+ /// placed in) are required to be mutable.
+ variable,
+
+ /// A (global) constant value is specified to be placed in a certain address space.
+ /// In contrast to .variable, values placed in this address space are not required to be mutable.
+ constant,
+
+ /// A pointer is ascripted to point into a certian address space.
+ pointer,
+};
+
+pub fn analyzeAddrspace(
+ sema: *Sema,
+ block: *Scope.Block,
+ src: LazySrcLoc,
+ zir_ref: Zir.Inst.Ref,
+ ctx: AddressSpaceContext,
+) !std.builtin.AddressSpace {
+ const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref);
+ const address_space = addrspace_tv.val.toEnum(std.builtin.AddressSpace);
+ const target = sema.mod.getTarget();
+ const arch = target.cpu.arch;
+
+ const supported = switch (address_space) {
+ .generic => true,
+ .gs, .fs, .ss => (arch == .i386 or arch == .x86_64) and ctx == .pointer,
+ };
+
+ if (!supported) {
+ // TODO error messages could be made more elaborate here
+ const entity = switch (ctx) {
+ .function => "functions",
+ .variable => "mutable values",
+ .constant => "constant values",
+ .pointer => "pointers",
+ };
+
+ return sema.mod.fail(
+ &block.base,
+ src,
+ "{s} with address space '{s}' are not supported on {s}",
+ .{ entity, @tagName(address_space), arch.genericName() },
+ );
+ }
+
+ return address_space;
+}
diff --git a/src/Zir.zig b/src/Zir.zig
@@ -443,10 +443,10 @@ pub const Inst = struct {
/// this instruction; a following 'ret' instruction will do the diversion.
/// Uses the `str_tok` union field.
ret_err_value_code,
- /// Create a pointer type that does not have a sentinel, alignment, or bit range specified.
+ /// Create a pointer type that does not have a sentinel, alignment, address space, or bit range specified.
/// Uses the `ptr_type_simple` union field.
ptr_type_simple,
- /// Create a pointer type which can have a sentinel, alignment, and/or bit range.
+ /// Create a pointer type which can have a sentinel, alignment, address space, and/or bit range.
/// Uses the `ptr_type` union field.
ptr_type,
/// Slice operation `lhs[rhs..]`. No sentinel and no end offset.
@@ -1672,6 +1672,7 @@ pub const Inst = struct {
atomic_order_type,
atomic_rmw_op_type,
calling_convention_type,
+ address_space_type,
float_mode_type,
reduce_op_type,
call_options_type,
@@ -1928,6 +1929,10 @@ pub const Inst = struct {
.ty = Type.initTag(.type),
.val = Value.initTag(.calling_convention_type),
},
+ .address_space_type = .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.address_space_type),
+ },
.float_mode_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.float_mode_type),
@@ -2129,8 +2134,9 @@ pub const Inst = struct {
is_volatile: bool,
has_sentinel: bool,
has_align: bool,
+ has_addrspace: bool,
has_bit_range: bool,
- _: u2 = undefined,
+ _: u1 = undefined,
},
size: std.builtin.TypeInfo.Pointer.Size,
/// Index into extra. See `PtrType`.
@@ -2360,12 +2366,13 @@ pub const Inst = struct {
else_body_len: u32,
};
- /// Stored in extra. Depending on the flags in Data, there will be up to 4
+ /// Stored in extra. Depending on the flags in Data, there will be up to 5
/// trailing Ref fields:
/// 0. sentinel: Ref // if `has_sentinel` flag is set
/// 1. align: Ref // if `has_align` flag is set
- /// 2. bit_start: Ref // if `has_bit_range` flag is set
- /// 3. bit_end: Ref // if `has_bit_range` flag is set
+ /// 2. address_space: Ref // if `has_addrspace` flag is set
+ /// 3. bit_start: Ref // if `has_bit_range` flag is set
+ /// 4. bit_end: Ref // if `has_bit_range` flag is set
pub const PtrType = struct {
elem_type: Ref,
};
@@ -2483,7 +2490,7 @@ pub const Inst = struct {
/// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression
- /// 0bX000: whether corresponding decl has a linksection expression
+ /// 0bX000: whether corresponding decl has a linksection or an address space expression
/// 5. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent
@@ -2495,7 +2502,10 @@ pub const Inst = struct {
/// this is a test decl, and the name starts at `name+1`.
/// value: Index,
/// align: Ref, // if corresponding bit is set
- /// link_section: Ref, // if corresponding bit is set
+ /// link_section_or_address_space: { // if corresponding bit is set.
+ /// link_section: Ref,
+ /// address_space: Ref,
+ /// }
/// }
/// 6. inst: Index // for every body_len
/// 7. flags: u32 // for every 8 fields
@@ -2547,7 +2557,7 @@ pub const Inst = struct {
/// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression
- /// 0bX000: whether corresponding decl has a linksection expression
+ /// 0bX000: whether corresponding decl has a linksection or an address space expression
/// 6. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent
@@ -2559,7 +2569,10 @@ pub const Inst = struct {
/// this is a test decl, and the name starts at `name+1`.
/// value: Index,
/// align: Ref, // if corresponding bit is set
- /// link_section: Ref, // if corresponding bit is set
+ /// link_section_or_address_space: { // if corresponding bit is set.
+ /// link_section: Ref,
+ /// address_space: Ref,
+ /// }
/// }
/// 7. inst: Index // for every body_len
/// 8. has_bits: u32 // for every 32 fields
@@ -2592,7 +2605,7 @@ pub const Inst = struct {
/// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression
- /// 0bX000: whether corresponding decl has a linksection expression
+ /// 0bX000: whether corresponding decl has a linksection or an address space expression
/// 6. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent
@@ -2604,7 +2617,10 @@ pub const Inst = struct {
/// this is a test decl, and the name starts at `name+1`.
/// value: Index,
/// align: Ref, // if corresponding bit is set
- /// link_section: Ref, // if corresponding bit is set
+ /// link_section_or_address_space: { // if corresponding bit is set.
+ /// link_section: Ref,
+ /// address_space: Ref,
+ /// }
/// }
/// 7. inst: Index // for every body_len
/// 8. has_bits: u32 // for every 8 fields
@@ -2641,7 +2657,7 @@ pub const Inst = struct {
/// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression
- /// 0bX000: whether corresponding decl has a linksection expression
+ /// 0bX000: whether corresponding decl has a linksection or an address space expression
/// 1. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent
@@ -2653,7 +2669,10 @@ pub const Inst = struct {
/// this is a test decl, and the name starts at `name+1`.
/// value: Index,
/// align: Ref, // if corresponding bit is set
- /// link_section: Ref, // if corresponding bit is set
+ /// link_section_or_address_space: { // if corresponding bit is set.
+ /// link_section: Ref,
+ /// address_space: Ref,
+ /// }
/// }
pub const OpaqueDecl = struct {
decls_len: u32,
diff --git a/src/codegen.zig b/src/codegen.zig
@@ -4895,7 +4895,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (typed_value.ty.zigTypeTag()) {
.Pointer => switch (typed_value.ty.ptrSize()) {
.Slice => {
- var buf: Type.Payload.ElemType = undefined;
+ var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
const slice_len = typed_value.val.sliceLen();
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
@@ -251,7 +251,7 @@ pub const DeclGen = struct {
try writer.writeByte('(');
try dg.renderType(writer, t);
try writer.writeAll("){");
- var buf: Type.Payload.ElemType = undefined;
+ var buf: Type.SlicePtrFieldTypeBuffer = undefined;
try dg.renderValue(writer, t.slicePtrFieldType(&buf), val);
try writer.writeAll(", ");
try writer.print("{d}", .{val.sliceLen()});
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
@@ -558,7 +558,8 @@ pub const DeclGen = struct {
llvm_params_len,
.False,
);
- const llvm_fn = self.llvmModule().addFunction(decl.name, fn_type);
+ const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace");
+ const llvm_fn = self.llvmModule().addFunctionInAddressSpace(decl.name, fn_type, llvm_addrspace);
const is_extern = decl.val.tag() == .extern_fn;
if (!is_extern) {
@@ -580,7 +581,24 @@ pub const DeclGen = struct {
if (llvm_module.getNamedGlobal(decl.name)) |val| return val;
// TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.
const llvm_type = try self.llvmType(decl.ty);
- return llvm_module.addGlobal(llvm_type, decl.name);
+ const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace");
+ return llvm_module.addGlobalInAddressSpace(llvm_type, decl.name, llvm_addrspace);
+ }
+
+ fn llvmAddressSpace(self: DeclGen, address_space: std.builtin.AddressSpace) c_uint {
+ const target = self.module.getTarget();
+ return switch (target.cpu.arch) {
+ .i386, .x86_64 => switch (address_space) {
+ .generic => llvm.address_space.default,
+ .gs => llvm.address_space.x86.gs,
+ .fs => llvm.address_space.x86.fs,
+ .ss => llvm.address_space.x86.ss,
+ },
+ else => switch (address_space) {
+ .generic => llvm.address_space.default,
+ else => unreachable,
+ },
+ };
}
fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
@@ -609,7 +627,7 @@ pub const DeclGen = struct {
.Bool => return self.context.intType(1),
.Pointer => {
if (t.isSlice()) {
- var buf: Type.Payload.ElemType = undefined;
+ var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const ptr_type = t.slicePtrFieldType(&buf);
const fields: [2]*const llvm.Type = .{
@@ -619,7 +637,8 @@ pub const DeclGen = struct {
return self.context.structType(&fields, fields.len, .False);
} else {
const elem_type = try self.llvmType(t.elemType());
- return elem_type.pointerType(0);
+ const llvm_addrspace = self.llvmAddressSpace(t.ptrAddressSpace());
+ return elem_type.pointerType(llvm_addrspace);
}
},
.Array => {
@@ -685,7 +704,9 @@ pub const DeclGen = struct {
@intCast(c_uint, llvm_params.len),
llvm.Bool.fromBool(is_var_args),
);
- return llvm_fn_ty.pointerType(0);
+ // TODO make .Fn not both a pointer type and a prototype
+ const llvm_addrspace = self.llvmAddressSpace(.generic);
+ return llvm_fn_ty.pointerType(llvm_addrspace);
},
.ComptimeInt => unreachable,
.ComptimeFloat => unreachable,
@@ -753,7 +774,7 @@ pub const DeclGen = struct {
.Pointer => switch (tv.val.tag()) {
.decl_ref => {
if (tv.ty.isSlice()) {
- var buf: Type.Payload.ElemType = undefined;
+ var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const ptr_ty = tv.ty.slicePtrFieldType(&buf);
var slice_len: Value.Payload.U64 = .{
.base = .{ .tag = .int_u64 },
@@ -783,12 +804,13 @@ pub const DeclGen = struct {
decl.alive = true;
const val = try self.resolveGlobalDecl(decl);
const llvm_var_type = try self.llvmType(tv.ty);
- const llvm_type = llvm_var_type.pointerType(0);
+ const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace");
+ const llvm_type = llvm_var_type.pointerType(llvm_addrspace);
return val.constBitCast(llvm_type);
},
.slice => {
const slice = tv.val.castTag(.slice).?.data;
- var buf: Type.Payload.ElemType = undefined;
+ var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const fields: [2]*const llvm.Value = .{
try self.genTypedValue(.{
.ty = tv.ty.slicePtrFieldType(&buf),
diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig
@@ -197,6 +197,9 @@ pub const Module = opaque {
pub const addFunction = LLVMAddFunction;
extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;
+ pub const addFunctionInAddressSpace = ZigLLVMAddFunctionInAddressSpace;
+ extern fn ZigLLVMAddFunctionInAddressSpace(*const Module, Name: [*:0]const u8, FunctionTy: *const Type, AddressSpace: c_uint) *const Value;
+
pub const getNamedFunction = LLVMGetNamedFunction;
extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value;
@@ -209,6 +212,9 @@ pub const Module = opaque {
pub const addGlobal = LLVMAddGlobal;
extern fn LLVMAddGlobal(M: *const Module, Ty: *const Type, Name: [*:0]const u8) *const Value;
+ pub const addGlobalInAddressSpace = LLVMAddGlobalInAddressSpace;
+ extern fn LLVMAddGlobalInAddressSpace(M: *const Module, Ty: *const Type, Name: [*:0]const u8, AddressSpace: c_uint) *const Value;
+
pub const getNamedGlobal = LLVMGetNamedGlobal;
extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value;
@@ -1005,3 +1011,65 @@ pub const TypeKind = enum(c_int) {
BFloat,
X86_AMX,
};
+
+pub const address_space = struct {
+ pub const default: c_uint = 0;
+
+ // See llvm/lib/Target/X86/X86.h
+ pub const x86_64 = x86;
+ pub const x86 = struct {
+ pub const gs: c_uint = 256;
+ pub const fs: c_uint = 257;
+ pub const ss: c_uint = 258;
+
+ pub const ptr32_sptr: c_uint = 270;
+ pub const ptr32_uptr: c_uint = 271;
+ pub const ptr64: c_uint = 272;
+ };
+
+ // See llvm/lib/Target/AVR/AVR.h
+ pub const avr = struct {
+ pub const data_memory: c_uint = 0;
+ pub const program_memory: c_uint = 1;
+ };
+
+ // See llvm/lib/Target/NVPTX/NVPTX.h
+ pub const nvptx = struct {
+ pub const generic: c_uint = 0;
+ pub const global: c_uint = 1;
+ pub const constant: c_uint = 2;
+ pub const shared: c_uint = 3;
+ pub const param: c_uint = 4;
+ pub const local: c_uint = 5;
+ };
+
+ // See llvm/lib/Target/AMDGPU/AMDGPU.h
+ pub const amdgpu = struct {
+ pub const flat: c_uint = 0;
+ pub const global: c_uint = 1;
+ pub const region: c_uint = 2;
+ pub const local: c_uint = 3;
+ pub const constant: c_uint = 4;
+ pub const private: c_uint = 5;
+ pub const constant_32bit: c_uint = 6;
+ pub const buffer_fat_pointer: c_uint = 7;
+ pub const param_d: c_uint = 6;
+ pub const param_i: c_uint = 7;
+ pub const constant_buffer_0: c_uint = 8;
+ pub const constant_buffer_1: c_uint = 9;
+ pub const constant_buffer_2: c_uint = 10;
+ pub const constant_buffer_3: c_uint = 11;
+ pub const constant_buffer_4: c_uint = 12;
+ pub const constant_buffer_5: c_uint = 13;
+ pub const constant_buffer_6: c_uint = 14;
+ pub const constant_buffer_7: c_uint = 15;
+ pub const constant_buffer_8: c_uint = 16;
+ pub const constant_buffer_9: c_uint = 17;
+ pub const constant_buffer_10: c_uint = 18;
+ pub const constant_buffer_11: c_uint = 19;
+ pub const constant_buffer_12: c_uint = 20;
+ pub const constant_buffer_13: c_uint = 21;
+ pub const constant_buffer_14: c_uint = 22;
+ pub const constant_buffer_15: c_uint = 23;
+ };
+};
diff --git a/src/print_zir.zig b/src/print_zir.zig
@@ -1147,7 +1147,7 @@ const Writer = struct {
cur_bit_bag >>= 1;
const has_align = @truncate(u1, cur_bit_bag) != 0;
cur_bit_bag >>= 1;
- const has_section = @truncate(u1, cur_bit_bag) != 0;
+ const has_section_or_addrspace = @truncate(u1, cur_bit_bag) != 0;
cur_bit_bag >>= 1;
const sub_index = extra_index;
@@ -1165,7 +1165,12 @@ const Writer = struct {
extra_index += 1;
break :inst inst;
};
- const section_inst: Zir.Inst.Ref = if (!has_section) .none else inst: {
+ const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
+ const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
+ extra_index += 1;
+ break :inst inst;
+ };
+ const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
extra_index += 1;
break :inst inst;
@@ -1196,6 +1201,11 @@ const Writer = struct {
try self.writeInstRef(stream, align_inst);
try stream.writeAll(")");
}
+ if (addrspace_inst != .none) {
+ try stream.writeAll(" addrspace(");
+ try self.writeInstRef(stream, addrspace_inst);
+ try stream.writeAll(")");
+ }
if (section_inst != .none) {
try stream.writeAll(" linksection(");
try self.writeInstRef(stream, section_inst);
diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp
@@ -86,6 +86,14 @@ enum CallingConvention {
CallingConventionSysV
};
+// Stage 1 supports only the generic address space
+enum AddressSpace {
+ AddressSpaceGeneric,
+ AddressSpaceGS,
+ AddressSpaceFS,
+ AddressSpaceSS,
+};
+
// This one corresponds to the builtin.zig enum.
enum BuiltinPtrSize {
BuiltinPtrSizeOne,
diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp
@@ -1019,6 +1019,16 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
zig_unreachable();
}
+const char *address_space_name(AddressSpace as) {
+ switch (as) {
+ case AddressSpaceGeneric: return "generic";
+ case AddressSpaceGS: return "gs";
+ case AddressSpaceFS: return "fs";
+ case AddressSpaceSS: return "ss";
+ }
+ zig_unreachable();
+}
+
ZigType *get_stack_trace_type(CodeGen *g) {
if (g->stack_trace_type == nullptr) {
g->stack_trace_type = get_builtin_type(g, "StackTrace");
diff --git a/src/stage1/analyze.hpp b/src/stage1/analyze.hpp
@@ -242,6 +242,8 @@ Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result);
bool calling_convention_allows_zig_types(CallingConvention cc);
const char *calling_convention_name(CallingConvention cc);
+const char *address_space_name(AddressSpace as);
+
Error ATTRIBUTE_MUST_USE file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents);
void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);
diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp
@@ -16124,7 +16124,7 @@ static Stage1AirInst *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira,
static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCtz *instruction) {
Error err;
-
+
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
if (type_is_invalid(int_type))
return ira->codegen->invalid_inst_gen;
@@ -16166,7 +16166,7 @@ static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCt
return ira->codegen->invalid_inst_gen;
if (val->special == ConstValSpecialUndef)
return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);
-
+
if (is_vector) {
ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);
Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, smallest_vec_type);
@@ -16200,7 +16200,7 @@ static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCt
static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstClz *instruction) {
Error err;
-
+
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
if (type_is_invalid(int_type))
return ira->codegen->invalid_inst_gen;
@@ -16242,7 +16242,7 @@ static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstCl
return ira->codegen->invalid_inst_gen;
if (val->special == ConstValSpecialUndef)
return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);
-
+
if (is_vector) {
ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);
Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, smallest_vec_type);
@@ -16276,7 +16276,7 @@ static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstCl
static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1ZirInstPopCount *instruction) {
Error err;
-
+
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
if (type_is_invalid(int_type))
return ira->codegen->invalid_inst_gen;
@@ -16318,7 +16318,7 @@ static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1Zir
return ira->codegen->invalid_inst_gen;
if (val->special == ConstValSpecialUndef)
return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);
-
+
if (is_vector) {
ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);
Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, smallest_vec_type);
@@ -17904,7 +17904,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, Scope *scope, AstNode
result->special = ConstValSpecialStatic;
result->type = type_info_pointer_type;
- ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 7);
+ ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 8);
result->data.x_struct.fields = fields;
// size: Size
@@ -17939,24 +17939,29 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, Scope *scope, AstNode
lazy_align_of->base.id = LazyValueIdAlignOf;
lazy_align_of->target_type = ir_const_type(ira, scope, source_node, attrs_type->data.pointer.child_type);
}
- // child: type
- ensure_field_index(result->type, "child", 4);
+ // address_space: AddressSpace,
+ ensure_field_index(result->type, "address_space", 4);
fields[4]->special = ConstValSpecialStatic;
- fields[4]->type = ira->codegen->builtin_types.entry_type;
- fields[4]->data.x_type = attrs_type->data.pointer.child_type;
- // is_allowzero: bool
- ensure_field_index(result->type, "is_allowzero", 5);
+ fields[4]->type = get_builtin_type(ira->codegen, "AddressSpace");
+ bigint_init_unsigned(&fields[4]->data.x_enum_tag, AddressSpaceGeneric);
+ // child: type
+ ensure_field_index(result->type, "child", 5);
fields[5]->special = ConstValSpecialStatic;
- fields[5]->type = ira->codegen->builtin_types.entry_bool;
- fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;
- // sentinel: anytype
- ensure_field_index(result->type, "sentinel", 6);
+ fields[5]->type = ira->codegen->builtin_types.entry_type;
+ fields[5]->data.x_type = attrs_type->data.pointer.child_type;
+ // is_allowzero: bool
+ ensure_field_index(result->type, "is_allowzero", 6);
fields[6]->special = ConstValSpecialStatic;
+ fields[6]->type = ira->codegen->builtin_types.entry_bool;
+ fields[6]->data.x_bool = attrs_type->data.pointer.allow_zero;
+ // sentinel: anytype
+ ensure_field_index(result->type, "sentinel", 7);
+ fields[7]->special = ConstValSpecialStatic;
if (attrs_type->data.pointer.sentinel != nullptr) {
- fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
- set_optional_payload(fields[6], attrs_type->data.pointer.sentinel);
+ fields[7]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
+ set_optional_payload(fields[7], attrs_type->data.pointer.sentinel);
} else {
- fields[6]->type = ira->codegen->builtin_types.entry_null;
+ fields[7]->type = ira->codegen->builtin_types.entry_null;
}
return result;
@@ -18465,7 +18470,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Fn", nullptr);
- ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 6);
+ ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 7);
result->data.x_struct.fields = fields;
// calling_convention: TypeInfo.CallingConvention
@@ -18826,11 +18831,11 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type));
BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag);
PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index);
- ZigType *elem_type = get_const_field_meta_type(ira, source_node, payload, "child", 4);
+ ZigType *elem_type = get_const_field_meta_type(ira, source_node, payload, "child", 5);
if (type_is_invalid(elem_type))
return ira->codegen->invalid_inst_gen->value->type;
ZigValue *sentinel;
- if ((err = get_const_field_sentinel(ira, scope, source_node, payload, "sentinel", 6,
+ if ((err = get_const_field_sentinel(ira, scope, source_node, payload, "sentinel", 7,
elem_type, &sentinel)))
{
return ira->codegen->invalid_inst_gen->value->type;
@@ -18845,6 +18850,19 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
if (alignment == nullptr)
return ira->codegen->invalid_inst_gen->value->type;
+ ZigValue *as_value = get_const_field(ira, source_node, payload, "address_space", 4);
+ if (as_value == nullptr)
+ return ira->codegen->invalid_inst_gen->value->type;
+ assert(as_value->special == ConstValSpecialStatic);
+ assert(as_value->type == get_builtin_type(ira->codegen, "AddressSpace"));
+ AddressSpace as = (AddressSpace)bigint_as_u32(&as_value->data.x_enum_tag);
+ if (as != AddressSpaceGeneric) {
+ ir_add_error_node(ira, source_node, buf_sprintf(
+ "address space '%s' not available in stage 1 compiler, must be .generic",
+ address_space_name(as)));
+ return ira->codegen->invalid_inst_gen->value->type;
+ }
+
bool is_const;
if ((err = get_const_field_bool(ira, source_node, payload, "is_const", 1, &is_const)))
return ira->codegen->invalid_inst_gen->value->type;
@@ -18857,13 +18875,12 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
}
bool is_allowzero;
- if ((err = get_const_field_bool(ira, source_node, payload, "is_allowzero", 5,
+ if ((err = get_const_field_bool(ira, source_node, payload, "is_allowzero", 6,
&is_allowzero)))
{
return ira->codegen->invalid_inst_gen->value->type;
}
-
ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen,
elem_type,
is_const,
diff --git a/src/target.zig b/src/target.zig
@@ -544,3 +544,21 @@ pub fn largestAtomicBits(target: std.Target) u32 {
.x86_64 => 128,
};
}
+
+pub fn defaultAddressSpace(
+ target: std.Target,
+ context: enum {
+ /// Query the default address space for global constant values.
+ global_constant,
+ /// Query the default address space for global mutable values.
+ global_mutable,
+ /// Query the default address space for function-local values.
+ local,
+ /// Query the default address space for functions themselves.
+ function,
+ },
+) std.builtin.AddressSpace {
+ _ = target;
+ _ = context;
+ return .generic;
+}
diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig
@@ -2614,6 +2614,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex {
.type_node = type_node,
.align_node = align_node,
.section_node = section_node,
+ .addrspace_node = 0,
}),
.rhs = init_node,
},
@@ -2705,6 +2706,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
.lhs = try c.addExtra(std.zig.Ast.Node.FnProtoOne{
.param = params.items[0],
.align_expr = align_expr,
+ .addrspace_expr = 0, // TODO
.section_expr = section_expr,
.callconv_expr = callconv_expr,
}),
@@ -2720,6 +2722,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
.params_start = span.start,
.params_end = span.end,
.align_expr = align_expr,
+ .addrspace_expr = 0, // TODO
.section_expr = section_expr,
.callconv_expr = callconv_expr,
}),
diff --git a/src/type.zig b/src/type.zig
@@ -127,6 +127,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
=> return .Enum,
@@ -288,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,
@@ -299,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,
@@ -310,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,
@@ -321,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,
@@ -332,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,
@@ -343,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,
@@ -354,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,
@@ -365,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,
@@ -376,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,
@@ -387,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,
@@ -398,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,
@@ -409,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,
@@ -461,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;
@@ -746,6 +761,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -835,6 +851,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",
@@ -958,6 +975,7 @@ pub const Type = extern union {
.atomic_order => return writer.writeAll("std.builtin.AtomicOrder"),
.atomic_rmw_op => return writer.writeAll("std.builtin.AtomicRmwOp"),
.calling_convention => return writer.writeAll("std.builtin.CallingConvention"),
+ .address_space => return writer.writeAll("std.builtin.AddressSpace"),
.float_mode => return writer.writeAll("std.builtin.FloatMode"),
.reduce_op => return writer.writeAll("std.builtin.ReduceOp"),
.call_options => return writer.writeAll("std.builtin.CallOptions"),
@@ -1111,6 +1129,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 ");
@@ -1186,6 +1207,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -1301,6 +1323,7 @@ pub const Type = extern union {
.atomic_order => return Value.initTag(.atomic_order_type),
.atomic_rmw_op => return Value.initTag(.atomic_rmw_op_type),
.calling_convention => return Value.initTag(.calling_convention_type),
+ .address_space => return Value.initTag(.address_space_type),
.float_mode => return Value.initTag(.float_mode_type),
.reduce_op => return Value.initTag(.reduce_op_type),
.call_options => return Value.initTag(.call_options_type),
@@ -1362,6 +1385,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -1496,6 +1520,30 @@ pub const Type = extern union {
}
}
+ pub fn ptrAddressSpace(self: Type) std.builtin.AddressSpace {
+ return switch (self.tag()) {
+ .single_const_pointer_to_comptime_int,
+ .const_slice_u8,
+ .single_const_pointer,
+ .single_mut_pointer,
+ .many_const_pointer,
+ .many_mut_pointer,
+ .c_const_pointer,
+ .c_mut_pointer,
+ .const_slice,
+ .mut_slice,
+ .inferred_alloc_const,
+ .inferred_alloc_mut,
+ .manyptr_u8,
+ .manyptr_const_u8,
+ => .generic,
+
+ .pointer => self.castTag(.pointer).?.data.@"addrspace",
+
+ else => unreachable,
+ };
+ }
+
/// Asserts that hasCodeGenBits() is true.
pub fn abiAlignment(self: Type, target: Target) u32 {
return switch (self.tag()) {
@@ -1508,6 +1556,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -1734,6 +1783,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -2019,6 +2069,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -2105,42 +2156,82 @@ pub const Type = extern union {
};
}
- pub fn slicePtrFieldType(self: Type, buffer: *Payload.ElemType) Type {
+ pub const SlicePtrFieldTypeBuffer = union {
+ elem_type: Payload.ElemType,
+ pointer: Payload.Pointer,
+ };
+
+ pub fn slicePtrFieldType(self: Type, buffer: *SlicePtrFieldTypeBuffer) Type {
switch (self.tag()) {
.const_slice_u8 => return Type.initTag(.manyptr_const_u8),
.const_slice => {
const elem_type = self.castTag(.const_slice).?.data;
buffer.* = .{
- .base = .{ .tag = .many_const_pointer },
- .data = elem_type,
+ .elem_type = .{
+ .base = .{ .tag = .many_const_pointer },
+ .data = elem_type,
+ },
};
- return Type.initPayload(&buffer.base);
+ return Type.initPayload(&buffer.elem_type.base);
},
.mut_slice => {
const elem_type = self.castTag(.mut_slice).?.data;
buffer.* = .{
- .base = .{ .tag = .many_mut_pointer },
- .data = elem_type,
+ .elem_type = .{
+ .base = .{ .tag = .many_mut_pointer },
+ .data = elem_type,
+ },
};
- return Type.initPayload(&buffer.base);
+ return Type.initPayload(&buffer.elem_type.base);
},
.pointer => {
const payload = self.castTag(.pointer).?.data;
assert(payload.size == .Slice);
- if (payload.mutable) {
+
+ if (payload.sentinel != null or
+ payload.@"align" != 0 or
+ payload.@"addrspace" != .generic or
+ payload.bit_offset != 0 or
+ payload.host_size != 0 or
+ payload.@"allowzero" or
+ payload.@"volatile")
+ {
buffer.* = .{
- .base = .{ .tag = .many_mut_pointer },
- .data = payload.pointee_type,
+ .pointer = .{
+ .data = .{
+ .pointee_type = payload.pointee_type,
+ .sentinel = payload.sentinel,
+ .@"align" = payload.@"align",
+ .@"addrspace" = payload.@"addrspace",
+ .bit_offset = payload.bit_offset,
+ .host_size = payload.host_size,
+ .@"allowzero" = payload.@"allowzero",
+ .mutable = payload.mutable,
+ .@"volatile" = payload.@"volatile",
+ .size = .Many,
+ },
+ },
};
+ return Type.initPayload(&buffer.pointer.base);
+ } else if (payload.mutable) {
+ buffer.* = .{
+ .elem_type = .{
+ .base = .{ .tag = .many_mut_pointer },
+ .data = payload.pointee_type,
+ },
+ };
+ return Type.initPayload(&buffer.elem_type.base);
} else {
buffer.* = .{
- .base = .{ .tag = .many_const_pointer },
- .data = payload.pointee_type,
+ .elem_type = .{
+ .base = .{ .tag = .many_const_pointer },
+ .data = payload.pointee_type,
+ },
};
+ return Type.initPayload(&buffer.elem_type.base);
}
- return Type.initPayload(&buffer.base);
},
else => unreachable,
@@ -2793,6 +2884,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -3000,6 +3092,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -3024,6 +3117,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -3047,6 +3141,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -3100,6 +3195,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -3155,6 +3251,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -3192,6 +3289,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -3242,6 +3340,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -3302,6 +3401,7 @@ pub const Type = extern union {
atomic_order,
atomic_rmw_op,
calling_convention,
+ address_space,
float_mode,
reduce_op,
call_options,
@@ -3425,6 +3525,7 @@ pub const Type = extern union {
.atomic_order,
.atomic_rmw_op,
.calling_convention,
+ .address_space,
.float_mode,
.reduce_op,
.call_options,
@@ -3580,6 +3681,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,
diff --git a/src/value.zig b/src/value.zig
@@ -63,6 +63,7 @@ pub const Value = extern union {
atomic_order_type,
atomic_rmw_op_type,
calling_convention_type,
+ address_space_type,
float_mode_type,
reduce_op_type,
call_options_type,
@@ -226,6 +227,7 @@ pub const Value = extern union {
.atomic_order_type,
.atomic_rmw_op_type,
.calling_convention_type,
+ .address_space_type,
.float_mode_type,
.reduce_op_type,
.call_options_type,
@@ -412,6 +414,7 @@ pub const Value = extern union {
.atomic_order_type,
.atomic_rmw_op_type,
.calling_convention_type,
+ .address_space_type,
.float_mode_type,
.reduce_op_type,
.call_options_type,
@@ -625,6 +628,7 @@ pub const Value = extern union {
.atomic_order_type => return out_stream.writeAll("std.builtin.AtomicOrder"),
.atomic_rmw_op_type => return out_stream.writeAll("std.builtin.AtomicRmwOp"),
.calling_convention_type => return out_stream.writeAll("std.builtin.CallingConvention"),
+ .address_space_type => return out_stream.writeAll("std.builtin.AddressSpace"),
.float_mode_type => return out_stream.writeAll("std.builtin.FloatMode"),
.reduce_op_type => return out_stream.writeAll("std.builtin.ReduceOp"),
.call_options_type => return out_stream.writeAll("std.builtin.CallOptions"),
@@ -792,6 +796,7 @@ pub const Value = extern union {
.atomic_order_type => Type.initTag(.atomic_order),
.atomic_rmw_op_type => Type.initTag(.atomic_rmw_op),
.calling_convention_type => Type.initTag(.calling_convention),
+ .address_space_type => Type.initTag(.address_space),
.float_mode_type => Type.initTag(.float_mode),
.reduce_op_type => Type.initTag(.reduce_op),
.call_options_type => Type.initTag(.call_options),
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
@@ -416,6 +416,11 @@ ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref) {
return wrap(Type::getTokenTy(*unwrap(context_ref)));
}
+LLVMValueRef ZigLLVMAddFunctionInAddressSpace(LLVMModuleRef M, const char *Name, LLVMTypeRef FunctionTy, unsigned AddressSpace) {
+ Function* func = Function::Create(unwrap<FunctionType>(FunctionTy), GlobalValue::ExternalLinkage, AddressSpace, Name, unwrap(M));
+ return wrap(func);
+}
+
LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
unsigned NumArgs, ZigLLVM_CallingConv CC, ZigLLVM_CallAttr attr, const char *Name)
{
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
@@ -65,6 +65,9 @@ ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, co
ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);
+ZIG_EXTERN_C LLVMValueRef ZigLLVMAddFunctionInAddressSpace(LLVMModuleRef M, const char *Name,
+ LLVMTypeRef FunctionTy, unsigned AddressSpace);
+
enum ZigLLVM_CallingConv {
ZigLLVM_C = 0,
ZigLLVM_Fast = 8,
diff --git a/test/behavior/type.zig b/test/behavior/type.zig
@@ -137,6 +137,7 @@ test "@Type create slice with null sentinel" {
.is_volatile = false,
.is_allowzero = false,
.alignment = 8,
+ .address_space = .generic,
.child = *i32,
.sentinel = null,
},
diff --git a/test/cases.zig b/test/cases.zig
@@ -1807,4 +1807,16 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
, "");
}
+
+ {
+ var case = ctx.exe("setting an address space on a local variable", linux_x64);
+ case.addError(
+ \\export fn entry() i32 {
+ \\ var foo: i32 addrspace(".general") = 1234;
+ \\ return foo;
+ \\}
+ , &[_][]const u8{
+ ":2:28: error: cannot set address space of local variable 'foo'",
+ });
+ }
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
@@ -711,6 +711,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ .is_const = false,
\\ .is_volatile = false,
\\ .alignment = 1,
+ \\ .address_space = .generic,
\\ .child = u8,
\\ .is_allowzero = false,
\\ .sentinel = 0,
@@ -720,6 +721,23 @@ pub fn addCases(ctx: *TestContext) !void {
"tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers",
});
+ ctx.objErrStage1("@Type(.Pointer) with invalid address space ",
+ \\export fn entry() void {
+ \\ _ = @Type(.{ .Pointer = .{
+ \\ .size = .One,
+ \\ .is_const = false,
+ \\ .is_volatile = false,
+ \\ .alignment = 1,
+ \\ .address_space = .gs,
+ \\ .child = u8,
+ \\ .is_allowzero = false,
+ \\ .sentinel = null,
+ \\ }});
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:2:16: error: address space 'gs' not available in stage 1 compiler, must be .generic",
+ });
+
ctx.testErrStage1("helpful return type error message",
\\export fn foo() u32 {
\\ return error.Ohno;
diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig
@@ -242,4 +242,184 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
, "");
}
+
+ {
+ var case = ctx.exeUsingLlvmBackend("invalid address space coercion", linux_x64);
+ case.addError(
+ \\fn entry(a: *addrspace(.gs) i32) *i32 {
+ \\ return a;
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ , &[_][]const u8{
+ ":2:12: error: expected *i32, found *addrspace(.gs) i32",
+ });
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("pointer keeps address space", linux_x64);
+ case.compiles(
+ \\fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 {
+ \\ return a;
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ );
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("pointer to explicit generic address space coerces to implicit pointer", linux_x64);
+ case.compiles(
+ \\fn entry(a: *addrspace(.generic) i32) *i32 {
+ \\ return a;
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ );
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("pointers with different address spaces", linux_x64);
+ case.addError(
+ \\fn entry(a: *addrspace(.gs) i32) *addrspace(.fs) i32 {
+ \\ return a;
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ , &[_][]const u8{
+ ":2:12: error: expected *addrspace(.fs) i32, found *addrspace(.gs) i32",
+ });
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("pointers with different address spaces", linux_x64);
+ case.addError(
+ \\fn entry(a: ?*addrspace(.gs) i32) *i32 {
+ \\ return a.?;
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ , &[_][]const u8{
+ ":2:13: error: expected *i32, found *addrspace(.gs) i32",
+ });
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("invalid pointer keeps address space when taking address of dereference", linux_x64);
+ case.addError(
+ \\fn entry(a: *addrspace(.gs) i32) *i32 {
+ \\ return &a.*;
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ , &[_][]const u8{
+ ":2:12: error: expected *i32, found *addrspace(.gs) i32",
+ });
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("pointer keeps address space when taking address of dereference", linux_x64);
+ case.compiles(
+ \\fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 {
+ \\ return &a.*;
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ );
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: array pointer", linux_x64);
+ case.compiles(
+ \\fn entry(a: *addrspace(.gs) [1]i32) *addrspace(.gs) i32 {
+ \\ return &a[0];
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ );
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: pointer to optional array", linux_x64);
+ case.compiles(
+ \\fn entry(a: *addrspace(.gs) ?[1]i32) *addrspace(.gs) i32 {
+ \\ return &a.*.?[0];
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ );
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: struct pointer", linux_x64);
+ case.compiles(
+ \\const A = struct{ a: i32 };
+ \\fn entry(a: *addrspace(.gs) A) *addrspace(.gs) i32 {
+ \\ return &a.a;
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ );
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: complex", linux_x64);
+ case.compiles(
+ \\const A = struct{ a: ?[1]i32 };
+ \\fn entry(a: *addrspace(.gs) [1]A) *addrspace(.gs) i32 {
+ \\ return &a[0].a.?[0];
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ );
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("dereferencing through multiple pointers with address spaces", linux_x64);
+ case.compiles(
+ \\fn entry(a: *addrspace(.fs) *addrspace(.gs) *i32) *i32 {
+ \\ return a.*.*;
+ \\}
+ \\pub export fn main() void { _ = entry; }
+ );
+ }
+
+ {
+ var case = ctx.exeUsingLlvmBackend("f segment address space reading and writing", linux_x64);
+ case.addCompareOutput(
+ \\fn assert(ok: bool) void {
+ \\ if (!ok) unreachable;
+ \\}
+ \\
+ \\fn setFs(value: c_ulong) void {
+ \\ asm volatile (
+ \\ \\syscall
+ \\ :
+ \\ : [number] "{rax}" (158),
+ \\ [code] "{rdi}" (0x1002),
+ \\ [val] "{rsi}" (value),
+ \\ : "rcx", "r11", "memory"
+ \\ );
+ \\}
+ \\
+ \\fn getFs() c_ulong {
+ \\ var result: c_ulong = undefined;
+ \\ asm volatile (
+ \\ \\syscall
+ \\ :
+ \\ : [number] "{rax}" (158),
+ \\ [code] "{rdi}" (0x1003),
+ \\ [ptr] "{rsi}" (@ptrToInt(&result)),
+ \\ : "rcx", "r11", "memory"
+ \\ );
+ \\ return result;
+ \\}
+ \\
+ \\var test_value: u64 = 12345;
+ \\
+ \\pub export fn main() c_int {
+ \\ const orig_fs = getFs();
+ \\
+ \\ setFs(@ptrToInt(&test_value));
+ \\ assert(getFs() == @ptrToInt(&test_value));
+ \\
+ \\ var test_ptr = @intToPtr(*allowzero addrspace(.fs) u64, 0);
+ \\ assert(test_ptr.* == 12345);
+ \\ test_ptr.* = 98765;
+ \\ assert(test_value == 98765);
+ \\
+ \\ setFs(orig_fs);
+ \\ return 0;
+ \\}
+ , "");
+ }
}