Merge pull request #9649 from Snektron/address-space

Address Spaces
This commit is contained in:
Andrew Kelley 2021-09-20 20:37:04 -04:00 committed by GitHub
commit 1ad905c71e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1177 additions and 224 deletions

View File

@ -901,6 +901,7 @@ fn tokenizeAndPrintRaw(
switch (token.tag) { switch (token.tag) {
.eof => break, .eof => break,
.keyword_addrspace,
.keyword_align, .keyword_align,
.keyword_and, .keyword_and,
.keyword_asm, .keyword_asm,

View File

@ -166,6 +166,15 @@ pub const CallingConvention = enum {
SysV, SysV,
}; };
/// 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 /// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation. /// therefore must be kept in sync with the compiler implementation.
pub const SourceLocation = struct { pub const SourceLocation = struct {
@ -226,6 +235,7 @@ pub const TypeInfo = union(enum) {
is_const: bool, is_const: bool,
is_volatile: bool, is_volatile: bool,
alignment: comptime_int, alignment: comptime_int,
address_space: AddressSpace,
child: type, child: type,
is_allowzero: bool, is_allowzero: bool,

View File

@ -2472,6 +2472,7 @@ fn CopyPtrAttrs(comptime source: type, comptime size: std.builtin.TypeInfo.Point
.is_volatile = info.is_volatile, .is_volatile = info.is_volatile,
.is_allowzero = info.is_allowzero, .is_allowzero = info.is_allowzero,
.alignment = info.alignment, .alignment = info.alignment,
.address_space = info.address_space,
.child = child, .child = child,
.sentinel = null, .sentinel = null,
}, },
@ -2960,6 +2961,7 @@ fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: u29) typ
.is_volatile = info.is_volatile, .is_volatile = info.is_volatile,
.is_allowzero = info.is_allowzero, .is_allowzero = info.is_allowzero,
.alignment = new_alignment, .alignment = new_alignment,
.address_space = info.address_space,
.child = info.child, .child = info.child,
.sentinel = null, .sentinel = null,
}, },

View File

@ -235,6 +235,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
.is_const = info.is_const, .is_const = info.is_const,
.is_volatile = info.is_volatile, .is_volatile = info.is_volatile,
.alignment = info.alignment, .alignment = info.alignment,
.address_space = info.address_space,
.child = @Type(.{ .child = @Type(.{
.Array = .{ .Array = .{
.len = array_info.len, .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_const = info.is_const,
.is_volatile = info.is_volatile, .is_volatile = info.is_volatile,
.alignment = info.alignment, .alignment = info.alignment,
.address_space = info.address_space,
.child = info.child, .child = info.child,
.is_allowzero = info.is_allowzero, .is_allowzero = info.is_allowzero,
.sentinel = sentinel_val, .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_const = ptr_info.is_const,
.is_volatile = ptr_info.is_volatile, .is_volatile = ptr_info.is_volatile,
.alignment = ptr_info.alignment, .alignment = ptr_info.alignment,
.address_space = ptr_info.address_space,
.child = ptr_info.child, .child = ptr_info.child,
.is_allowzero = ptr_info.is_allowzero, .is_allowzero = ptr_info.is_allowzero,
.sentinel = sentinel_val, .sentinel = sentinel_val,

View File

@ -262,6 +262,9 @@ pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {
token_tags[parse_error.token].symbol(), token_tags[parse_error.token].symbol(),
}); });
}, },
.extra_addrspace_qualifier => {
return stream.writeAll("extra addrspace qualifier");
},
.extra_align_qualifier => { .extra_align_qualifier => {
return stream.writeAll("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 => { .fn_proto_one => {
const extra = tree.extraData(datas[n].lhs, Node.FnProtoOne); 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. // find the last one here.
var max_node: Node.Index = datas[n].rhs; var max_node: Node.Index = datas[n].rhs;
var max_start = token_starts[main_tokens[max_node]]; 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 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) { if (extra.section_expr != 0) {
const start = token_starts[main_tokens[extra.section_expr]]; const start = token_starts[main_tokens[extra.section_expr]];
if (start > max_start) { if (start > max_start) {
@ -1055,7 +1066,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
}, },
.fn_proto => { .fn_proto => {
const extra = tree.extraData(datas[n].lhs, Node.FnProto); 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. // find the last one here.
var max_node: Node.Index = datas[n].rhs; var max_node: Node.Index = datas[n].rhs;
var max_start = token_starts[main_tokens[max_node]]; 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 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) { if (extra.section_expr != 0) {
const start = token_starts[main_tokens[extra.section_expr]]; const start = token_starts[main_tokens[extra.section_expr]];
if (start > max_start) { if (start > max_start) {
@ -1138,6 +1157,7 @@ pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
return tree.fullVarDecl(.{ return tree.fullVarDecl(.{
.type_node = extra.type_node, .type_node = extra.type_node,
.align_node = extra.align_node, .align_node = extra.align_node,
.addrspace_node = extra.addrspace_node,
.section_node = extra.section_node, .section_node = extra.section_node,
.init_node = data.rhs, .init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node], .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(.{ return tree.fullVarDecl(.{
.type_node = extra.type_node, .type_node = extra.type_node,
.align_node = extra.align_node, .align_node = extra.align_node,
.addrspace_node = 0,
.section_node = 0, .section_node = 0,
.init_node = data.rhs, .init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node], .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(.{ return tree.fullVarDecl(.{
.type_node = data.lhs, .type_node = data.lhs,
.align_node = 0, .align_node = 0,
.addrspace_node = 0,
.section_node = 0, .section_node = 0,
.init_node = data.rhs, .init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node], .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(.{ return tree.fullVarDecl(.{
.type_node = 0, .type_node = 0,
.align_node = data.lhs, .align_node = data.lhs,
.addrspace_node = 0,
.section_node = 0, .section_node = 0,
.init_node = data.rhs, .init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node], .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, .return_type = data.rhs,
.params = params, .params = params,
.align_expr = 0, .align_expr = 0,
.addrspace_expr = 0,
.section_expr = 0, .section_expr = 0,
.callconv_expr = 0, .callconv_expr = 0,
}); });
@ -1265,6 +1289,7 @@ pub fn fnProtoMulti(tree: Tree, node: Node.Index) full.FnProto {
.return_type = data.rhs, .return_type = data.rhs,
.params = params, .params = params,
.align_expr = 0, .align_expr = 0,
.addrspace_expr = 0,
.section_expr = 0, .section_expr = 0,
.callconv_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, .return_type = data.rhs,
.params = params, .params = params,
.align_expr = extra.align_expr, .align_expr = extra.align_expr,
.addrspace_expr = extra.addrspace_expr,
.section_expr = extra.section_expr, .section_expr = extra.section_expr,
.callconv_expr = extra.callconv_expr, .callconv_expr = extra.callconv_expr,
}); });
@ -1298,6 +1324,7 @@ pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto {
.return_type = data.rhs, .return_type = data.rhs,
.params = params, .params = params,
.align_expr = extra.align_expr, .align_expr = extra.align_expr,
.addrspace_expr = extra.addrspace_expr,
.section_expr = extra.section_expr, .section_expr = extra.section_expr,
.callconv_expr = extra.callconv_expr, .callconv_expr = extra.callconv_expr,
}); });
@ -1453,6 +1480,7 @@ pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType {
return tree.fullPtrType(.{ return tree.fullPtrType(.{
.main_token = tree.nodes.items(.main_token)[node], .main_token = tree.nodes.items(.main_token)[node],
.align_node = data.lhs, .align_node = data.lhs,
.addrspace_node = 0,
.sentinel = 0, .sentinel = 0,
.bit_range_start = 0, .bit_range_start = 0,
.bit_range_end = 0, .bit_range_end = 0,
@ -1466,6 +1494,7 @@ pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType {
return tree.fullPtrType(.{ return tree.fullPtrType(.{
.main_token = tree.nodes.items(.main_token)[node], .main_token = tree.nodes.items(.main_token)[node],
.align_node = 0, .align_node = 0,
.addrspace_node = 0,
.sentinel = data.lhs, .sentinel = data.lhs,
.bit_range_start = 0, .bit_range_start = 0,
.bit_range_end = 0, .bit_range_end = 0,
@ -1480,6 +1509,7 @@ pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType {
return tree.fullPtrType(.{ return tree.fullPtrType(.{
.main_token = tree.nodes.items(.main_token)[node], .main_token = tree.nodes.items(.main_token)[node],
.align_node = extra.align_node, .align_node = extra.align_node,
.addrspace_node = extra.addrspace_node,
.sentinel = extra.sentinel, .sentinel = extra.sentinel,
.bit_range_start = 0, .bit_range_start = 0,
.bit_range_end = 0, .bit_range_end = 0,
@ -1494,6 +1524,7 @@ pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType {
return tree.fullPtrType(.{ return tree.fullPtrType(.{
.main_token = tree.nodes.items(.main_token)[node], .main_token = tree.nodes.items(.main_token)[node],
.align_node = extra.align_node, .align_node = extra.align_node,
.addrspace_node = extra.addrspace_node,
.sentinel = extra.sentinel, .sentinel = extra.sentinel,
.bit_range_start = extra.bit_range_start, .bit_range_start = extra.bit_range_start,
.bit_range_end = extra.bit_range_end, .bit_range_end = extra.bit_range_end,
@ -2063,6 +2094,7 @@ pub const full = struct {
mut_token: TokenIndex, mut_token: TokenIndex,
type_node: Node.Index, type_node: Node.Index,
align_node: Node.Index, align_node: Node.Index,
addrspace_node: Node.Index,
section_node: Node.Index, section_node: Node.Index,
init_node: Node.Index, init_node: Node.Index,
}; };
@ -2130,6 +2162,7 @@ pub const full = struct {
return_type: Node.Index, return_type: Node.Index,
params: []const Node.Index, params: []const Node.Index,
align_expr: Node.Index, align_expr: Node.Index,
addrspace_expr: Node.Index,
section_expr: Node.Index, section_expr: Node.Index,
callconv_expr: Node.Index, callconv_expr: Node.Index,
}; };
@ -2288,6 +2321,7 @@ pub const full = struct {
pub const Components = struct { pub const Components = struct {
main_token: TokenIndex, main_token: TokenIndex,
align_node: Node.Index, align_node: Node.Index,
addrspace_node: Node.Index,
sentinel: Node.Index, sentinel: Node.Index,
bit_range_start: Node.Index, bit_range_start: Node.Index,
bit_range_end: Node.Index, bit_range_end: Node.Index,
@ -2397,6 +2431,7 @@ pub const Error = struct {
expected_var_decl_or_fn, expected_var_decl_or_fn,
expected_loop_payload, expected_loop_payload,
expected_container, expected_container,
extra_addrspace_qualifier,
extra_align_qualifier, extra_align_qualifier,
extra_allowzero_qualifier, extra_allowzero_qualifier,
extra_const_qualifier, extra_const_qualifier,
@ -2723,13 +2758,13 @@ pub const Node = struct {
/// main_token is the `fn` keyword. /// main_token is the `fn` keyword.
/// extern function declarations use this tag. /// extern function declarations use this tag.
fn_proto_multi, 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. /// zero or one parameters.
/// anytype and ... parameters are omitted from the AST tree. /// anytype and ... parameters are omitted from the AST tree.
/// main_token is the `fn` keyword. /// main_token is the `fn` keyword.
/// extern function declarations use this tag. /// extern function declarations use this tag.
fn_proto_one, 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. /// anytype and ... parameters are omitted from the AST tree.
/// main_token is the `fn` keyword. /// main_token is the `fn` keyword.
/// extern function declarations use this tag. /// extern function declarations use this tag.
@ -2893,11 +2928,13 @@ pub const Node = struct {
pub const PtrType = struct { pub const PtrType = struct {
sentinel: Index, sentinel: Index,
align_node: Index, align_node: Index,
addrspace_node: Index,
}; };
pub const PtrTypeBitRange = struct { pub const PtrTypeBitRange = struct {
sentinel: Index, sentinel: Index,
align_node: Index, align_node: Index,
addrspace_node: Index,
bit_range_start: Index, bit_range_start: Index,
bit_range_end: Index, bit_range_end: Index,
}; };
@ -2920,8 +2957,13 @@ pub const Node = struct {
}; };
pub const GlobalVarDecl = struct { pub const GlobalVarDecl = struct {
/// Populated if there is an explicit type ascription.
type_node: Index, type_node: Index,
/// Populated if align(A) is present.
align_node: Index, align_node: Index,
/// Populated if addrspace(A) is present.
addrspace_node: Index,
/// Populated if linksection(A) is present.
section_node: Index, section_node: Index,
}; };
@ -2953,6 +2995,8 @@ pub const Node = struct {
param: Index, param: Index,
/// Populated if align(A) is present. /// Populated if align(A) is present.
align_expr: Index, align_expr: Index,
/// Populated if addrspace(A) is present.
addrspace_expr: Index,
/// Populated if linksection(A) is present. /// Populated if linksection(A) is present.
section_expr: Index, section_expr: Index,
/// Populated if callconv(A) is present. /// Populated if callconv(A) is present.
@ -2964,6 +3008,8 @@ pub const Node = struct {
params_end: Index, params_end: Index,
/// Populated if align(A) is present. /// Populated if align(A) is present.
align_expr: Index, align_expr: Index,
/// Populated if addrspace(A) is present.
addrspace_expr: Index,
/// Populated if linksection(A) is present. /// Populated if linksection(A) is present.
section_expr: Index, section_expr: Index,
/// Populated if callconv(A) is present. /// Populated if callconv(A) is present.

View File

@ -325,6 +325,7 @@ pub fn FlexibleArrayType(comptime SelfType: type, ElementType: type) type {
.is_const = ptr.is_const, .is_const = ptr.is_const,
.is_volatile = ptr.is_volatile, .is_volatile = ptr.is_volatile,
.alignment = @alignOf(ElementType), .alignment = @alignOf(ElementType),
.address_space = .generic,
.child = ElementType, .child = ElementType,
.is_allowzero = true, .is_allowzero = true,
.sentinel = null, .sentinel = null,

View File

@ -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 { fn parseFnProto(p: *Parser) !Node.Index {
const fn_token = p.eatToken(.keyword_fn) orelse return null_node; const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
@ -639,6 +639,7 @@ const Parser = struct {
_ = p.eatToken(.identifier); _ = p.eatToken(.identifier);
const params = try p.parseParamDeclList(); const params = try p.parseParamDeclList();
const align_expr = try p.parseByteAlign(); const align_expr = try p.parseByteAlign();
const addrspace_expr = try p.parseAddrSpace();
const section_expr = try p.parseLinkSection(); const section_expr = try p.parseLinkSection();
const callconv_expr = try p.parseCallconv(); const callconv_expr = try p.parseCallconv();
_ = p.eatToken(.bang); _ = p.eatToken(.bang);
@ -650,7 +651,7 @@ const Parser = struct {
try p.warn(.expected_return_type); 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) { switch (params) {
.zero_or_one => |param| return p.setNode(fn_proto_index, .{ .zero_or_one => |param| return p.setNode(fn_proto_index, .{
.tag = .fn_proto_simple, .tag = .fn_proto_simple,
@ -683,6 +684,7 @@ const Parser = struct {
.lhs = try p.addExtra(Node.FnProtoOne{ .lhs = try p.addExtra(Node.FnProtoOne{
.param = param, .param = param,
.align_expr = align_expr, .align_expr = align_expr,
.addrspace_expr = addrspace_expr,
.section_expr = section_expr, .section_expr = section_expr,
.callconv_expr = callconv_expr, .callconv_expr = callconv_expr,
}), }),
@ -698,6 +700,7 @@ const Parser = struct {
.params_start = span.start, .params_start = span.start,
.params_end = span.end, .params_end = span.end,
.align_expr = align_expr, .align_expr = align_expr,
.addrspace_expr = addrspace_expr,
.section_expr = section_expr, .section_expr = section_expr,
.callconv_expr = callconv_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 { fn parseVarDecl(p: *Parser) !Node.Index {
const mut_token = p.eatToken(.keyword_const) orelse const mut_token = p.eatToken(.keyword_const) orelse
p.eatToken(.keyword_var) orelse p.eatToken(.keyword_var) orelse
@ -717,9 +720,10 @@ const Parser = struct {
_ = try p.expectToken(.identifier); _ = try p.expectToken(.identifier);
const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr(); const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr();
const align_node = try p.parseByteAlign(); const align_node = try p.parseByteAlign();
const addrspace_node = try p.parseAddrSpace();
const section_node = try p.parseLinkSection(); const section_node = try p.parseLinkSection();
const init_node: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr(); 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) { if (align_node == 0) {
return p.addNode(.{ return p.addNode(.{
.tag = .simple_var_decl, .tag = .simple_var_decl,
@ -759,6 +763,7 @@ const Parser = struct {
.lhs = try p.addExtra(Node.GlobalVarDecl{ .lhs = try p.addExtra(Node.GlobalVarDecl{
.type_node = type_node, .type_node = type_node,
.align_node = align_node, .align_node = align_node,
.addrspace_node = addrspace_node,
.section_node = section_node, .section_node = section_node,
}), }),
.rhs = init_node, .rhs = init_node,
@ -1440,8 +1445,8 @@ const Parser = struct {
/// PrefixTypeOp /// PrefixTypeOp
/// <- QUESTIONMARK /// <- QUESTIONMARK
/// / KEYWORD_anyframe MINUSRARROW /// / KEYWORD_anyframe MINUSRARROW
/// / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* /// / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
/// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* /// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
/// / ArrayTypeStart /// / ArrayTypeStart
/// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET
/// PtrTypeStart /// PtrTypeStart
@ -1474,16 +1479,7 @@ const Parser = struct {
const asterisk = p.nextToken(); const asterisk = p.nextToken();
const mods = try p.parsePtrModifiers(); const mods = try p.parsePtrModifiers();
const elem_type = try p.expectTypeExpr(); 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,
.main_token = asterisk,
.data = .{
.lhs = mods.align_node,
.rhs = elem_type,
},
});
} else {
return p.addNode(.{ return p.addNode(.{
.tag = .ptr_type_bit_range, .tag = .ptr_type_bit_range,
.main_token = asterisk, .main_token = asterisk,
@ -1491,12 +1487,35 @@ const Parser = struct {
.lhs = try p.addExtra(Node.PtrTypeBitRange{ .lhs = try p.addExtra(Node.PtrTypeBitRange{
.sentinel = 0, .sentinel = 0,
.align_node = mods.align_node, .align_node = mods.align_node,
.addrspace_node = mods.addrspace_node,
.bit_range_start = mods.bit_range_start, .bit_range_start = mods.bit_range_start,
.bit_range_end = mods.bit_range_end, .bit_range_end = mods.bit_range_end,
}), }),
.rhs = elem_type, .rhs = elem_type,
}, },
}); });
} else if (mods.addrspace_node != 0) {
return p.addNode(.{
.tag = .ptr_type,
.main_token = asterisk,
.data = .{
.lhs = try p.addExtra(Node.PtrType{
.sentinel = 0,
.align_node = mods.align_node,
.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 => { .asterisk_asterisk => {
@ -1504,16 +1523,7 @@ const Parser = struct {
const mods = try p.parsePtrModifiers(); const mods = try p.parsePtrModifiers();
const elem_type = try p.expectTypeExpr(); const elem_type = try p.expectTypeExpr();
const inner: Node.Index = inner: { 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,
.main_token = asterisk,
.data = .{
.lhs = mods.align_node,
.rhs = elem_type,
},
});
} else {
break :inner try p.addNode(.{ break :inner try p.addNode(.{
.tag = .ptr_type_bit_range, .tag = .ptr_type_bit_range,
.main_token = asterisk, .main_token = asterisk,
@ -1521,12 +1531,35 @@ const Parser = struct {
.lhs = try p.addExtra(Node.PtrTypeBitRange{ .lhs = try p.addExtra(Node.PtrTypeBitRange{
.sentinel = 0, .sentinel = 0,
.align_node = mods.align_node, .align_node = mods.align_node,
.addrspace_node = mods.addrspace_node,
.bit_range_start = mods.bit_range_start, .bit_range_start = mods.bit_range_start,
.bit_range_end = mods.bit_range_end, .bit_range_end = mods.bit_range_end,
}), }),
.rhs = elem_type, .rhs = elem_type,
}, },
}); });
} else if (mods.addrspace_node != 0) {
break :inner try p.addNode(.{
.tag = .ptr_type,
.main_token = asterisk,
.data = .{
.lhs = try p.addExtra(Node.PtrType{
.sentinel = 0,
.align_node = mods.align_node,
.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(.{ return p.addNode(.{
@ -1560,7 +1593,7 @@ const Parser = struct {
const mods = try p.parsePtrModifiers(); const mods = try p.parsePtrModifiers();
const elem_type = try p.expectTypeExpr(); const elem_type = try p.expectTypeExpr();
if (mods.bit_range_start == 0) { if (mods.bit_range_start == 0) {
if (sentinel == 0) { if (sentinel == 0 and mods.addrspace_node == 0) {
return p.addNode(.{ return p.addNode(.{
.tag = .ptr_type_aligned, .tag = .ptr_type_aligned,
.main_token = asterisk, .main_token = asterisk,
@ -1569,7 +1602,7 @@ const Parser = struct {
.rhs = elem_type, .rhs = elem_type,
}, },
}); });
} else if (mods.align_node == 0) { } else if (mods.align_node == 0 and mods.addrspace_node == 0) {
return p.addNode(.{ return p.addNode(.{
.tag = .ptr_type_sentinel, .tag = .ptr_type_sentinel,
.main_token = asterisk, .main_token = asterisk,
@ -1586,6 +1619,7 @@ const Parser = struct {
.lhs = try p.addExtra(Node.PtrType{ .lhs = try p.addExtra(Node.PtrType{
.sentinel = sentinel, .sentinel = sentinel,
.align_node = mods.align_node, .align_node = mods.align_node,
.addrspace_node = mods.addrspace_node,
}), }),
.rhs = elem_type, .rhs = elem_type,
}, },
@ -1599,6 +1633,7 @@ const Parser = struct {
.lhs = try p.addExtra(Node.PtrTypeBitRange{ .lhs = try p.addExtra(Node.PtrTypeBitRange{
.sentinel = sentinel, .sentinel = sentinel,
.align_node = mods.align_node, .align_node = mods.align_node,
.addrspace_node = mods.addrspace_node,
.bit_range_start = mods.bit_range_start, .bit_range_start = mods.bit_range_start,
.bit_range_end = mods.bit_range_end, .bit_range_end = mods.bit_range_end,
}), }),
@ -1624,7 +1659,7 @@ const Parser = struct {
.token = p.nodes.items(.main_token)[mods.bit_range_start], .token = p.nodes.items(.main_token)[mods.bit_range_start],
}); });
} }
if (sentinel == 0) { if (sentinel == 0 and mods.addrspace_node == 0) {
return p.addNode(.{ return p.addNode(.{
.tag = .ptr_type_aligned, .tag = .ptr_type_aligned,
.main_token = lbracket, .main_token = lbracket,
@ -1633,7 +1668,7 @@ const Parser = struct {
.rhs = elem_type, .rhs = elem_type,
}, },
}); });
} else if (mods.align_node == 0) { } else if (mods.align_node == 0 and mods.addrspace_node == 0) {
return p.addNode(.{ return p.addNode(.{
.tag = .ptr_type_sentinel, .tag = .ptr_type_sentinel,
.main_token = lbracket, .main_token = lbracket,
@ -1650,6 +1685,7 @@ const Parser = struct {
.lhs = try p.addExtra(Node.PtrType{ .lhs = try p.addExtra(Node.PtrType{
.sentinel = sentinel, .sentinel = sentinel,
.align_node = mods.align_node, .align_node = mods.align_node,
.addrspace_node = mods.addrspace_node,
}), }),
.rhs = elem_type, .rhs = elem_type,
}, },
@ -1661,6 +1697,7 @@ const Parser = struct {
.keyword_const, .keyword_const,
.keyword_volatile, .keyword_volatile,
.keyword_allowzero, .keyword_allowzero,
.keyword_addrspace,
=> return p.fail(.ptr_mod_on_array_child_type), => return p.fail(.ptr_mod_on_array_child_type),
else => {}, else => {},
} }
@ -2879,6 +2916,15 @@ const Parser = struct {
return expr_node; 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 /// ParamDecl
/// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType /// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
/// / DOT3 /// / DOT3
@ -3011,6 +3057,7 @@ const Parser = struct {
const PtrModifiers = struct { const PtrModifiers = struct {
align_node: Node.Index, align_node: Node.Index,
addrspace_node: Node.Index,
bit_range_start: Node.Index, bit_range_start: Node.Index,
bit_range_end: Node.Index, bit_range_end: Node.Index,
}; };
@ -3018,12 +3065,14 @@ const Parser = struct {
fn parsePtrModifiers(p: *Parser) !PtrModifiers { fn parsePtrModifiers(p: *Parser) !PtrModifiers {
var result: PtrModifiers = .{ var result: PtrModifiers = .{
.align_node = 0, .align_node = 0,
.addrspace_node = 0,
.bit_range_start = 0, .bit_range_start = 0,
.bit_range_end = 0, .bit_range_end = 0,
}; };
var saw_const = false; var saw_const = false;
var saw_volatile = false; var saw_volatile = false;
var saw_allowzero = false; var saw_allowzero = false;
var saw_addrspace = false;
while (true) { while (true) {
switch (p.token_tags[p.tok_i]) { switch (p.token_tags[p.tok_i]) {
.keyword_align => { .keyword_align => {
@ -3063,6 +3112,12 @@ const Parser = struct {
p.tok_i += 1; p.tok_i += 1;
saw_allowzero = true; saw_allowzero = true;
}, },
.keyword_addrspace => {
if (saw_addrspace) {
try p.warn(.extra_addrspace_qualifier);
}
result.addrspace_node = try p.parseAddrSpace();
},
else => return result, else => return result,
} }
} }

View File

@ -404,6 +404,10 @@ test "zig fmt: trailing comma in fn parameter list" {
\\pub fn f( \\pub fn f(
\\ a: i32, \\ a: i32,
\\ b: i32, \\ b: i32,
\\) addrspace(.generic) i32 {}
\\pub fn f(
\\ a: i32,
\\ b: i32,
\\) linksection(".text") i32 {} \\) linksection(".text") i32 {}
\\pub fn f( \\pub fn f(
\\ a: i32, \\ a: i32,
@ -553,8 +557,8 @@ test "zig fmt: sentinel-terminated slice type" {
test "zig fmt: pointer-to-one with modifiers" { test "zig fmt: pointer-to-one with modifiers" {
try testCanonical( try testCanonical(
\\const x: *u32 = undefined; \\const x: *u32 = undefined;
\\const y: *allowzero align(8) const volatile u32 = undefined; \\const y: *allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\const z: *allowzero align(8:4:2) 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" { test "zig fmt: pointer-to-many with modifiers" {
try testCanonical( try testCanonical(
\\const x: [*]u32 = undefined; \\const x: [*]u32 = undefined;
\\const y: [*]allowzero align(8) const volatile u32 = undefined; \\const y: [*]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\const z: [*]allowzero align(8:4:2) 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" { test "zig fmt: sentinel pointer with modifiers" {
try testCanonical( try testCanonical(
\\const x: [*:42]u32 = undefined; \\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;
\\const y: [*:42]allowzero align(8:4:2) 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" { test "zig fmt: c pointer with modifiers" {
try testCanonical( try testCanonical(
\\const x: [*c]u32 = undefined; \\const x: [*c]u32 = undefined;
\\const y: [*c]allowzero align(8) const volatile u32 = undefined; \\const y: [*c]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\const z: [*c]allowzero align(8:4:2) 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" { test "zig fmt: slice with modifiers" {
try testCanonical( try testCanonical(
\\const x: []u32 = undefined; \\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" { test "zig fmt: sentinel slice with modifiers" {
try testCanonical( try testCanonical(
\\const x: [:42]u32 = undefined; \\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" { test "zig fmt: correctly space struct fields with doc comments" {
try testTransform( try testTransform(
\\pub const S = struct { \\pub const S = struct {

View File

@ -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| { if (ptr_type.const_token) |const_token| {
try renderToken(ais, tree, const_token, .space); 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 const name_space = if (var_decl.ast.type_node == 0 and
(var_decl.ast.align_node != 0 or (var_decl.ast.align_node != 0 or
var_decl.ast.addrspace_node != 0 or
var_decl.ast.section_node != 0 or var_decl.ast.section_node != 0 or
var_decl.ast.init_node != 0)) var_decl.ast.init_node != 0))
Space.space 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) { if (var_decl.ast.type_node != 0) {
try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // : 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 if (var_decl.ast.align_node != 0 or var_decl.ast.addrspace_node != 0 or
var_decl.ast.init_node != 0) var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0)
{ {
try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space); try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space);
} else { } 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, align_kw, Space.none); // align
try renderToken(ais, tree, lparen, Space.none); // ( try renderToken(ais, tree, lparen, Space.none); // (
try renderExpression(gpa, ais, tree, var_decl.ast.align_node, 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) { if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) {
try renderToken(ais, tree, rparen, .space); // ) try renderToken(ais, tree, rparen, .space); // )
} else { } else {
@ -1267,6 +1293,14 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro
smallest_start = start; 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) { if (fn_proto.ast.section_expr != 0) {
const tok = tree.firstToken(fn_proto.ast.section_expr) - 3; const tok = tree.firstToken(fn_proto.ast.section_expr) - 3;
const start = token_starts[tok]; 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); // ) 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) { if (fn_proto.ast.section_expr != 0) {
const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1; const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;
const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1; const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;

View File

@ -11,6 +11,7 @@ pub const Token = struct {
}; };
pub const keywords = std.ComptimeStringMap(Tag, .{ pub const keywords = std.ComptimeStringMap(Tag, .{
.{ "addrspace", .keyword_addrspace },
.{ "align", .keyword_align }, .{ "align", .keyword_align },
.{ "allowzero", .keyword_allowzero }, .{ "allowzero", .keyword_allowzero },
.{ "and", .keyword_and }, .{ "and", .keyword_and },
@ -132,6 +133,7 @@ pub const Token = struct {
float_literal, float_literal,
doc_comment, doc_comment,
container_doc_comment, container_doc_comment,
keyword_addrspace,
keyword_align, keyword_align,
keyword_allowzero, keyword_allowzero,
keyword_and, keyword_and,
@ -251,6 +253,7 @@ pub const Token = struct {
.angle_bracket_angle_bracket_right => ">>", .angle_bracket_angle_bracket_right => ">>",
.angle_bracket_angle_bracket_right_equal => ">>=", .angle_bracket_angle_bracket_right_equal => ">>=",
.tilde => "~", .tilde => "~",
.keyword_addrspace => "addrspace",
.keyword_align => "align", .keyword_align => "align",
.keyword_allowzero => "allowzero", .keyword_allowzero => "allowzero",
.keyword_and => "and", .keyword_and => "and",

View File

@ -1116,6 +1116,11 @@ fn fnProtoExpr(
const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { 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); 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) { if (fn_proto.ast.section_expr != 0) {
return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{}); 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 gpa = astgen.gpa;
const tree = astgen.tree; const tree = astgen.tree;
const token_tags = tree.tokens.items(.tag); 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 name_token = var_decl.ast.mut_token + 1;
const ident_name_raw = tree.tokenSlice(name_token); const ident_name_raw = tree.tokenSlice(name_token);
@ -2385,6 +2391,14 @@ fn varDecl(
return astgen.failNode(node, "variables must be initialized", .{}); 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) const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
try expr(gz, scope, align_rl, var_decl.ast.align_node) try expr(gz, scope, align_rl, var_decl.ast.align_node)
else else
@ -2714,6 +2728,7 @@ fn ptrType(
const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type); const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
const simple = ptr_info.ast.align_node == 0 and 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.sentinel == 0 and
ptr_info.ast.bit_range_start == 0; ptr_info.ast.bit_range_start == 0;
@ -2732,6 +2747,7 @@ fn ptrType(
var sentinel_ref: Zir.Inst.Ref = .none; var sentinel_ref: Zir.Inst.Ref = .none;
var align_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_start_ref: Zir.Inst.Ref = .none;
var bit_end_ref: Zir.Inst.Ref = .none; var bit_end_ref: Zir.Inst.Ref = .none;
var trailing_count: u32 = 0; var trailing_count: u32 = 0;
@ -2744,6 +2760,10 @@ fn ptrType(
align_ref = try expr(gz, scope, align_rl, ptr_info.ast.align_node); align_ref = try expr(gz, scope, align_rl, ptr_info.ast.align_node);
trailing_count += 1; 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) { if (ptr_info.ast.bit_range_start != 0) {
assert(ptr_info.ast.bit_range_end != 0); assert(ptr_info.ast.bit_range_end != 0);
bit_start_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_start); bit_start_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_start);
@ -2764,6 +2784,9 @@ fn ptrType(
if (align_ref != .none) { if (align_ref != .none) {
gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref)); gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref));
} }
if (addrspace_ref != .none) {
gz.astgen.extra.appendAssumeCapacity(@enumToInt(addrspace_ref));
}
if (bit_start_ref != .none) { if (bit_start_ref != .none) {
gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref)); gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref)); gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
@ -2779,6 +2802,7 @@ fn ptrType(
.is_volatile = ptr_info.volatile_token != null, .is_volatile = ptr_info.volatile_token != null,
.has_sentinel = sentinel_ref != .none, .has_sentinel = sentinel_ref != .none,
.has_align = align_ref != .none, .has_align = align_ref != .none,
.has_addrspace = addrspace_ref != .none,
.has_bit_range = bit_start_ref != .none, .has_bit_range = bit_start_ref != .none,
}, },
.size = ptr_info.size, .size = ptr_info.size,
@ -2847,7 +2871,7 @@ const WipDecls = struct {
is_pub: bool, is_pub: bool,
is_export: bool, is_export: bool,
has_align: bool, has_align: bool,
has_section: bool, has_section_or_addrspace: bool,
) Allocator.Error!void { ) Allocator.Error!void {
if (wip_decls.decl_index % fields_per_u32 == 0 and wip_decls.decl_index != 0) { 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); 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_pub)) << 28) |
(@as(u32, @boolToInt(is_export)) << 29) | (@as(u32, @boolToInt(is_export)) << 29) |
(@as(u32, @boolToInt(has_align)) << 30) | (@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; 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; const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
break :blk token_tags[maybe_inline_token] == .keyword_inline; 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; var params_scope = &fn_gz.base;
const is_var_args = is_var_args: { 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: { 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); 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: { 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); 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.addBreak(.break_inline, block_inst, func_inst);
try decl_gz.setBlockBody(block_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 contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
const casted = @bitCast([4]u32, contents_hash); const casted = @bitCast([4]u32, contents_hash);
@ -3127,8 +3155,10 @@ fn fnDecl(
if (align_inst != .none) { if (align_inst != .none) {
wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst)); 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(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: { 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); 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: { 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); 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: { const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
if (!is_mutable) { if (!is_mutable) {
@ -3256,7 +3290,7 @@ fn globalVarDecl(
_ = try block_scope.addBreak(.break_inline, block_inst, var_inst); _ = try block_scope.addBreak(.break_inline, block_inst, var_inst);
try block_scope.setBlockBody(block_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 contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
const casted = @bitCast([4]u32, contents_hash); const casted = @bitCast([4]u32, contents_hash);
@ -3271,8 +3305,9 @@ fn globalVarDecl(
if (align_inst != .none) { if (align_inst != .none) {
wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst)); 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(section_inst));
wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst));
} }
} }

View File

@ -288,6 +288,8 @@ pub const Decl = struct {
align_val: Value, align_val: Value,
/// Populated when `has_tv`. /// Populated when `has_tv`.
linksection_val: Value, linksection_val: Value,
/// Populated when `has_tv`.
@"addrspace": std.builtin.AddressSpace,
/// The memory for ty, val, align_val, linksection_val. /// The memory for ty, val, align_val, linksection_val.
/// If this is `null` then there is no memory management needed. /// If this is `null` then there is no memory management needed.
value_arena: ?*std.heap.ArenaAllocator.State = null, value_arena: ?*std.heap.ArenaAllocator.State = null,
@ -351,7 +353,7 @@ pub const Decl = struct {
/// to require re-analysis. /// to require re-analysis.
outdated, 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, has_tv: bool,
/// If `true` it means the `Decl` is the resource owner of the type/value associated /// 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 /// with it. That means when `Decl` is destroyed, the cleanup code should additionally
@ -366,8 +368,8 @@ pub const Decl = struct {
is_exported: bool, is_exported: bool,
/// Whether the ZIR code provides an align instruction. /// Whether the ZIR code provides an align instruction.
has_align: bool, has_align: bool,
/// Whether the ZIR code provides a linksection instruction. /// Whether the ZIR code provides a linksection and address space instruction.
has_linksection: bool, has_linksection_or_addrspace: bool,
/// Flag used by garbage collection to mark and sweep. /// Flag used by garbage collection to mark and sweep.
/// Decls which correspond to an AST node always have this field set to `true`. /// 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 /// 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; if (!decl.has_align) return .none;
assert(decl.zir_decl_index != 0); assert(decl.zir_decl_index != 0);
const zir = decl.namespace.file_scope.zir; 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 { 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); assert(decl.zir_decl_index != 0);
const zir = decl.namespace.file_scope.zir; 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]); 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_pub = true;
new_decl.is_exported = false; new_decl.is_exported = false;
new_decl.has_align = 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.ty = struct_ty;
new_decl.val = struct_val; new_decl.val = struct_val;
new_decl.has_tv = true; 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); if (linksection_ref == .none) break :blk Value.initTag(.null_value);
break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val; 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 // 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), // is a struct, for example, this resolves `type` (which needs no resolution),
// not the struct itself. // 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.val = try decl_tv.val.copy(&decl_arena.allocator);
decl.align_val = try align_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.linksection_val = try linksection_val.copy(&decl_arena.allocator);
decl.@"addrspace" = address_space;
decl.has_tv = true; decl.has_tv = true;
decl.owns_tv = owns_tv; decl.owns_tv = owns_tv;
decl_arena_state.* = decl_arena.state; 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.val = try decl_tv.val.copy(&decl_arena.allocator);
decl.align_val = try align_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.linksection_val = try linksection_val.copy(&decl_arena.allocator);
decl.@"addrspace" = address_space;
decl.has_tv = true; decl.has_tv = true;
decl_arena_state.* = decl_arena.state; decl_arena_state.* = decl_arena.state;
decl.value_arena = decl_arena_state; decl.value_arena = decl_arena_state;
@ -3526,8 +3556,8 @@ pub fn scanNamespace(
const decl_sub_index = extra_index; const decl_sub_index = extra_index;
extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1) extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1)
extra_index += @truncate(u1, flags >> 2); extra_index += @truncate(u1, flags >> 2); // Align
extra_index += @truncate(u1, flags >> 3); 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); try scanDecl(&scan_decl_iter, decl_sub_index, flags);
} }
@ -3556,7 +3586,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
const is_pub = (flags & 0b0001) != 0; const is_pub = (flags & 0b0001) != 0;
const export_bit = (flags & 0b0010) != 0; const export_bit = (flags & 0b0010) != 0;
const has_align = (flags & 0b0100) != 0; const has_align = (flags & 0b0100) != 0;
const has_linksection = (flags & 0b1000) != 0; const has_linksection_or_addrspace = (flags & 0b1000) != 0;
// zig fmt: on // zig fmt: on
const line = iter.parent_decl.relativeToLine(zir.extra[decl_sub_index + 4]); 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_exported = is_exported;
new_decl.is_usingnamespace = is_usingnamespace; new_decl.is_usingnamespace = is_usingnamespace;
new_decl.has_align = has_align; 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.zir_decl_index = @intCast(u32, decl_sub_index);
new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive. new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive.
return; return;
@ -3656,7 +3686,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
decl.is_exported = is_exported; decl.is_exported = is_exported;
decl.is_usingnamespace = is_usingnamespace; decl.is_usingnamespace = is_usingnamespace;
decl.has_align = has_align; 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); decl.zir_decl_index = @intCast(u32, decl_sub_index);
if (decl.getFunction()) |_| { if (decl.getFunction()) |_| {
switch (mod.comp.bin_file.tag) { switch (mod.comp.bin_file.tag) {
@ -4028,6 +4058,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
.val = undefined, .val = undefined,
.align_val = undefined, .align_val = undefined,
.linksection_val = undefined, .linksection_val = undefined,
.@"addrspace" = undefined,
.analysis = .unreferenced, .analysis = .unreferenced,
.deletion_flag = false, .deletion_flag = false,
.zir_decl_index = 0, .zir_decl_index = 0,
@ -4052,7 +4083,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
.generation = 0, .generation = 0,
.is_pub = false, .is_pub = false,
.is_exported = false, .is_exported = false,
.has_linksection = false, .has_linksection_or_addrspace = false,
.has_align = false, .has_align = false,
.alive = false, .alive = false,
.is_usingnamespace = false, .is_usingnamespace = false,
@ -4185,6 +4216,9 @@ pub fn createAnonymousDeclFromDeclNamed(
new_decl.src_line = owner_decl.src_line; new_decl.src_line = owner_decl.src_line;
new_decl.ty = typed_value.ty; new_decl.ty = typed_value.ty;
new_decl.val = typed_value.val; 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.has_tv = true;
new_decl.analysis = .complete; new_decl.analysis = .complete;
new_decl.generation = mod.generation; new_decl.generation = mod.generation;
@ -4330,10 +4364,59 @@ pub fn simplePtrType(
elem_ty: Type, elem_ty: Type,
mutable: bool, mutable: bool,
size: std.builtin.TypeInfo.Pointer.Size, size: std.builtin.TypeInfo.Pointer.Size,
@"addrspace": std.builtin.AddressSpace,
) Allocator.Error!Type { ) 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))) { if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
return Type.initTag(.const_slice_u8); return Type.initTag(.const_slice_u8);
} }
// TODO stage1 type inference bug // TODO stage1 type inference bug
const T = Type.Tag; const T = Type.Tag;
@ -4352,34 +4435,6 @@ pub fn simplePtrType(
return Type.initPayload(&type_payload.base); 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 { pub fn optionalType(arena: *Allocator, child_type: Type) Allocator.Error!Type {
switch (child_type.tag()) { switch (child_type.tag()) {
.single_const_pointer => return Type.Tag.optional_single_const_pointer.create( .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_file = (mod.importPkg(builtin_pkg) catch unreachable).file;
const builtin_namespace = builtin_file.root_decl.?.namespace; const builtin_namespace = builtin_file.root_decl.?.namespace;
const decl = builtin_namespace.decls.get("test_functions").?; 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 tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType();
const array_decl = d: { const array_decl = d: {

View File

@ -1373,7 +1373,13 @@ fn zirRetPtr(
return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty); 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); 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 ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
const var_decl_src = inst_data.src(); const var_decl_src = inst_data.src();
const var_type = try sema.resolveType(block, ty_src, inst_data.operand); 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); try sema.requireRuntimeBlock(block, var_decl_src);
return block.addTy(.alloc, ptr_type); 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); return sema.analyzeComptimeAlloc(block, var_type);
} }
try sema.validateVarType(block, ty_src, 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); try sema.requireRuntimeBlock(block, var_decl_src);
return block.addTy(.alloc, ptr_type); 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); try sema.mod.declareDeclDependency(sema.owner_decl, decl);
const final_elem_ty = try decl.ty.copy(sema.arena); 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); 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; 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); try sema.validateVarType(block, ty_src, final_elem_ty);
} }
// Change it to a normal alloc. // 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, .{ sema.air_instructions.set(ptr_inst, .{
.tag = .alloc, .tag = .alloc,
.data = .{ .ty = final_ptr_ty }, .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 ptr = sema.resolveInst(bin_inst.lhs);
const value = sema.resolveInst(bin_inst.rhs); 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, // 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. // if expressions should force it when the condition is compile-time known.
const src: LazySrcLoc = .unneeded; const src: LazySrcLoc = .unneeded;
@ -1821,7 +1858,14 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
// for the inferred allocation. // for the inferred allocation.
try inferred_alloc.data.stored_inst_list.append(sema.arena, operand); try inferred_alloc.data.stored_inst_list.append(sema.arena, operand);
// Create a runtime bitcast instruction with exactly the type the pointer wants. // 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); const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr);
return sema.storePtr(block, src, bitcasted_ptr, operand); 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_pub = module_fn.owner_decl.is_pub;
new_decl.is_exported = module_fn.owner_decl.is_exported; new_decl.is_exported = module_fn.owner_decl.is_exported;
new_decl.has_align = module_fn.owner_decl.has_align; 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.zir_decl_index = module_fn.owner_decl.zir_decl_index;
new_decl.alive = true; // This Decl is called at runtime. new_decl.alive = true; // This Decl is called at runtime.
new_decl.has_tv = true; new_decl.has_tv = true;
@ -3658,7 +3702,13 @@ fn zirOptionalPayloadPtr(
} }
const child_type = try opt_type.optionalChildAlloc(sema.arena); 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 sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
if (try pointer_val.pointerDeref(sema.arena)) |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()}); return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand_ty.elemType()});
const payload_ty = operand_ty.elemType().errorUnionPayload(); 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 sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
if (try pointer_val.pointerDeref(sema.arena)) |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, elem_type,
null, null,
0, 0,
.generic,
0, 0,
0, 0,
inst_data.is_mutable, 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); break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32);
} else 0; } 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 bit_start = if (inst_data.flags.has_bit_range) blk: {
const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
extra_i += 1; extra_i += 1;
@ -6933,6 +6996,7 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
elem_type, elem_type,
sentinel, sentinel,
abi_align, abi_align,
address_space,
bit_start, bit_start,
bit_end, bit_end,
inst_data.flags.is_mutable, inst_data.flags.is_mutable,
@ -8339,7 +8403,13 @@ fn panicWithMsg(
const panic_fn = try sema.getBuiltin(block, src, "panic"); const panic_fn = try sema.getBuiltin(block, src, "panic");
const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); 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 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( const null_stack_trace = try sema.addConstant(
try Module.optionalType(arena, ptr_stack_trace_ty), try Module.optionalType(arena, ptr_stack_trace_ty),
Value.initTag(.null_value), Value.initTag(.null_value),
@ -8423,7 +8493,7 @@ fn fieldVal(
.Pointer => switch (object_ty.ptrSize()) { .Pointer => switch (object_ty.ptrSize()) {
.Slice => { .Slice => {
if (mem.eql(u8, field_name, "ptr")) { 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); const result_ty = object_ty.slicePtrFieldType(buf);
if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| { if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
if (val.isUndef()) return sema.addConstUndef(result_ty); if (val.isUndef()) return sema.addConstUndef(result_ty);
@ -8457,12 +8527,13 @@ fn fieldVal(
} }
}, },
.One => { .One => {
const elem_ty = object_ty.elemType(); const ptr_child = object_ty.elemType();
if (elem_ty.zigTypeTag() == .Array) { switch (ptr_child.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) { if (mem.eql(u8, field_name, "len")) {
return sema.addConstant( return sema.addConstant(
Type.initTag(.comptime_int), Type.initTag(.comptime_int),
try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()), try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
); );
} else { } else {
return mod.fail( return mod.fail(
@ -8472,6 +8543,16 @@ fn fieldVal(
.{ field_name, object_ty }, .{ 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 => {}, .Many, .C => {},
@ -8595,9 +8676,8 @@ fn fieldPtr(
); );
} }
}, },
.Pointer => { .Pointer => switch (object_ty.ptrSize()) {
const ptr_child = object_ty.elemType(); .Slice => {
if (ptr_child.isSlice()) {
// Here for the ptr and len fields what we need to do is the situation // 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`. // 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 // This value may be known at compile-time or runtime. In the former
@ -8627,7 +8707,10 @@ fn fieldPtr(
.{ field_name, object_ty }, .{ field_name, object_ty },
); );
} }
} else switch (ptr_child.zigTypeTag()) { },
.One => {
const ptr_child = object_ty.elemType();
switch (ptr_child.zigTypeTag()) {
.Array => { .Array => {
if (mem.eql(u8, field_name, "len")) { if (mem.eql(u8, field_name, "len")) {
var anon_decl = try block.startAnonDecl(); var anon_decl = try block.startAnonDecl();
@ -8645,9 +8728,19 @@ fn fieldPtr(
); );
} }
}, },
.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 => {}, else => {},
} }
}, },
.Many, .C => {},
},
.Type => { .Type => {
_ = try sema.resolveConstValue(block, object_ptr_src, object_ptr); _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);
const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
@ -8788,13 +8881,20 @@ fn structFieldPtr(
const arena = sema.arena; const arena = sema.arena;
assert(unresolved_struct_ty.zigTypeTag() == .Struct); 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_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);
const struct_obj = struct_ty.castTag(.@"struct").?.data; const struct_obj = struct_ty.castTag(.@"struct").?.data;
const field_index = struct_obj.fields.getIndex(field_name) orelse const field_index = struct_obj.fields.getIndex(field_name) orelse
return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name); return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
const field = struct_obj.fields.values()[field_index]; 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| { if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
return sema.addConstant( return sema.addConstant(
@ -8885,6 +8985,7 @@ fn unionFieldPtr(
const arena = sema.arena; const arena = sema.arena;
assert(unresolved_union_ty.zigTypeTag() == .Union); 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_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
const union_obj = union_ty.cast(Type.Payload.Union).?.data; 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); return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
const field = union_obj.fields.values()[field_index]; 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| { if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {
// TODO detect inactive union field and emit compile error // TODO detect inactive union field and emit compile error
@ -9068,10 +9175,13 @@ fn elemPtrArray(
) CompileError!Air.Inst.Ref { ) CompileError!Air.Inst.Ref {
const array_ptr_ty = sema.typeOf(array_ptr); const array_ptr_ty = sema.typeOf(array_ptr);
const pointee_type = array_ptr_ty.elemType().elemType(); const pointee_type = array_ptr_ty.elemType().elemType();
const result_ty = if (array_ptr_ty.ptrIsMutable()) const result_ty = try Module.simplePtrType(
try Type.Tag.single_mut_pointer.create(sema.arena, pointee_type) sema.arena,
else pointee_type,
try Type.Tag.single_const_pointer.create(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, src, array_ptr)) |array_ptr_val| {
if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_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(); const dest_is_mut = !dest_type.isConstPtr();
if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr; 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.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(); const dst_elem_type = dest_type.elemType();
switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut)) { 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; 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 const ok_sent = dest_info.sentinel == null or src_info.size == .C or
(src_info.sentinel != null and (src_info.sentinel != null and
dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type)); 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(); const decl_tv = try decl.typedValue();
if (decl_tv.val.castTag(.variable)) |payload| { if (decl_tv.val.castTag(.variable)) |payload| {
const variable = payload.data; 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(ty, try Value.Tag.decl_ref.create(sema.arena, decl));
} }
return sema.addConstant( 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), try Value.Tag.decl_ref.create(sema.arena, decl),
); );
} }
@ -9617,8 +9732,9 @@ fn analyzeRef(
} }
try sema.requireRuntimeBlock(block, src); try sema.requireRuntimeBlock(block, src);
const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One); const address_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local);
const mut_ptr_type = try Module.simplePtrType(sema.arena, operand_ty, true, .One); 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); const alloc = try block.addTy(.alloc, mut_ptr_type);
try sema.storePtr(block, src, alloc, operand); try sema.storePtr(block, src, alloc, operand);
@ -9779,6 +9895,7 @@ fn analyzeSlice(
return_elem_type, return_elem_type,
if (end_opt == .none) slice_sentinel else null, if (end_opt == .none) slice_sentinel else null,
0, // TODO alignment 0, // TODO alignment
if (ptr_child.zigTypeTag() == .Pointer) ptr_child.ptrAddressSpace() else .generic,
0, 0,
0, 0,
!ptr_child.isConstPtr(), !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_order => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrder"),
.atomic_rmw_op => return sema.resolveBuiltinTypeFields(block, src, "AtomicRmwOp"), .atomic_rmw_op => return sema.resolveBuiltinTypeFields(block, src, "AtomicRmwOp"),
.calling_convention => return sema.resolveBuiltinTypeFields(block, src, "CallingConvention"), .calling_convention => return sema.resolveBuiltinTypeFields(block, src, "CallingConvention"),
.address_space => return sema.resolveBuiltinTypeFields(block, src, "AddressSpace"),
.float_mode => return sema.resolveBuiltinTypeFields(block, src, "FloatMode"), .float_mode => return sema.resolveBuiltinTypeFields(block, src, "FloatMode"),
.reduce_op => return sema.resolveBuiltinTypeFields(block, src, "ReduceOp"), .reduce_op => return sema.resolveBuiltinTypeFields(block, src, "ReduceOp"),
.call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"), .call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"),
@ -10680,6 +10798,7 @@ fn typeHasOnePossibleValue(
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -10865,6 +10984,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
.atomic_order => return .atomic_order_type, .atomic_order => return .atomic_order_type,
.atomic_rmw_op => return .atomic_rmw_op_type, .atomic_rmw_op => return .atomic_rmw_op_type,
.calling_convention => return .calling_convention_type, .calling_convention => return .calling_convention_type,
.address_space => return .address_space_type,
.float_mode => return .float_mode_type, .float_mode => return .float_mode_type,
.reduce_op => return .reduce_op_type, .reduce_op => return .reduce_op_type,
.call_options => return .call_options_type, .call_options => return .call_options_type,
@ -10960,7 +11080,13 @@ fn analyzeComptimeAlloc(
block: *Scope.Block, block: *Scope.Block,
var_type: Type, var_type: Type,
) CompileError!Air.Inst.Ref { ) 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(); var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit(); defer anon_decl.deinit();
@ -10976,3 +11102,58 @@ fn analyzeComptimeAlloc(
.decl = decl, .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;
}

View File

@ -443,10 +443,10 @@ pub const Inst = struct {
/// this instruction; a following 'ret' instruction will do the diversion. /// this instruction; a following 'ret' instruction will do the diversion.
/// Uses the `str_tok` union field. /// Uses the `str_tok` union field.
ret_err_value_code, 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. /// Uses the `ptr_type_simple` union field.
ptr_type_simple, 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. /// Uses the `ptr_type` union field.
ptr_type, ptr_type,
/// Slice operation `lhs[rhs..]`. No sentinel and no end offset. /// Slice operation `lhs[rhs..]`. No sentinel and no end offset.
@ -1672,6 +1672,7 @@ pub const Inst = struct {
atomic_order_type, atomic_order_type,
atomic_rmw_op_type, atomic_rmw_op_type,
calling_convention_type, calling_convention_type,
address_space_type,
float_mode_type, float_mode_type,
reduce_op_type, reduce_op_type,
call_options_type, call_options_type,
@ -1928,6 +1929,10 @@ pub const Inst = struct {
.ty = Type.initTag(.type), .ty = Type.initTag(.type),
.val = Value.initTag(.calling_convention_type), .val = Value.initTag(.calling_convention_type),
}, },
.address_space_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.address_space_type),
},
.float_mode_type = .{ .float_mode_type = .{
.ty = Type.initTag(.type), .ty = Type.initTag(.type),
.val = Value.initTag(.float_mode_type), .val = Value.initTag(.float_mode_type),
@ -2129,8 +2134,9 @@ pub const Inst = struct {
is_volatile: bool, is_volatile: bool,
has_sentinel: bool, has_sentinel: bool,
has_align: bool, has_align: bool,
has_addrspace: bool,
has_bit_range: bool, has_bit_range: bool,
_: u2 = undefined, _: u1 = undefined,
}, },
size: std.builtin.TypeInfo.Pointer.Size, size: std.builtin.TypeInfo.Pointer.Size,
/// Index into extra. See `PtrType`. /// Index into extra. See `PtrType`.
@ -2360,12 +2366,13 @@ pub const Inst = struct {
else_body_len: u32, 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: /// trailing Ref fields:
/// 0. sentinel: Ref // if `has_sentinel` flag is set /// 0. sentinel: Ref // if `has_sentinel` flag is set
/// 1. align: Ref // if `has_align` flag is set /// 1. align: Ref // if `has_align` flag is set
/// 2. bit_start: Ref // if `has_bit_range` flag is set /// 2. address_space: Ref // if `has_addrspace` flag is set
/// 3. bit_end: Ref // if `has_bit_range` 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 { pub const PtrType = struct {
elem_type: Ref, elem_type: Ref,
}; };
@ -2483,7 +2490,7 @@ pub const Inst = struct {
/// 0b000X: whether corresponding decl is pub /// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported /// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression /// 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 /// 5. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes /// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent /// 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`. /// this is a test decl, and the name starts at `name+1`.
/// value: Index, /// value: Index,
/// align: Ref, // if corresponding bit is set /// 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 /// 6. inst: Index // for every body_len
/// 7. flags: u32 // for every 8 fields /// 7. flags: u32 // for every 8 fields
@ -2547,7 +2557,7 @@ pub const Inst = struct {
/// 0b000X: whether corresponding decl is pub /// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported /// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression /// 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 /// 6. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes /// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent /// 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`. /// this is a test decl, and the name starts at `name+1`.
/// value: Index, /// value: Index,
/// align: Ref, // if corresponding bit is set /// 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 /// 7. inst: Index // for every body_len
/// 8. has_bits: u32 // for every 32 fields /// 8. has_bits: u32 // for every 32 fields
@ -2592,7 +2605,7 @@ pub const Inst = struct {
/// 0b000X: whether corresponding decl is pub /// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported /// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression /// 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 /// 6. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes /// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent /// 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`. /// this is a test decl, and the name starts at `name+1`.
/// value: Index, /// value: Index,
/// align: Ref, // if corresponding bit is set /// 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 /// 7. inst: Index // for every body_len
/// 8. has_bits: u32 // for every 8 fields /// 8. has_bits: u32 // for every 8 fields
@ -2641,7 +2657,7 @@ pub const Inst = struct {
/// 0b000X: whether corresponding decl is pub /// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported /// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression /// 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 /// 1. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes /// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent /// 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`. /// this is a test decl, and the name starts at `name+1`.
/// value: Index, /// value: Index,
/// align: Ref, // if corresponding bit is set /// 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 { pub const OpaqueDecl = struct {
decls_len: u32, decls_len: u32,

View File

@ -4895,7 +4895,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (typed_value.ty.zigTypeTag()) { switch (typed_value.ty.zigTypeTag()) {
.Pointer => switch (typed_value.ty.ptrSize()) { .Pointer => switch (typed_value.ty.ptrSize()) {
.Slice => { .Slice => {
var buf: Type.Payload.ElemType = undefined; var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const ptr_type = typed_value.ty.slicePtrFieldType(&buf); const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val }); const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
const slice_len = typed_value.val.sliceLen(); const slice_len = typed_value.val.sliceLen();

View File

@ -251,7 +251,7 @@ pub const DeclGen = struct {
try writer.writeByte('('); try writer.writeByte('(');
try dg.renderType(writer, t); try dg.renderType(writer, t);
try writer.writeAll("){"); try writer.writeAll("){");
var buf: Type.Payload.ElemType = undefined; var buf: Type.SlicePtrFieldTypeBuffer = undefined;
try dg.renderValue(writer, t.slicePtrFieldType(&buf), val); try dg.renderValue(writer, t.slicePtrFieldType(&buf), val);
try writer.writeAll(", "); try writer.writeAll(", ");
try writer.print("{d}", .{val.sliceLen()}); try writer.print("{d}", .{val.sliceLen()});

View File

@ -558,7 +558,8 @@ pub const DeclGen = struct {
llvm_params_len, llvm_params_len,
.False, .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; const is_extern = decl.val.tag() == .extern_fn;
if (!is_extern) { if (!is_extern) {
@ -580,7 +581,24 @@ pub const DeclGen = struct {
if (llvm_module.getNamedGlobal(decl.name)) |val| return val; if (llvm_module.getNamedGlobal(decl.name)) |val| return val;
// TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`. // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.
const llvm_type = try self.llvmType(decl.ty); 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 { 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), .Bool => return self.context.intType(1),
.Pointer => { .Pointer => {
if (t.isSlice()) { if (t.isSlice()) {
var buf: Type.Payload.ElemType = undefined; var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const ptr_type = t.slicePtrFieldType(&buf); const ptr_type = t.slicePtrFieldType(&buf);
const fields: [2]*const llvm.Type = .{ const fields: [2]*const llvm.Type = .{
@ -619,7 +637,8 @@ pub const DeclGen = struct {
return self.context.structType(&fields, fields.len, .False); return self.context.structType(&fields, fields.len, .False);
} else { } else {
const elem_type = try self.llvmType(t.elemType()); 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 => { .Array => {
@ -685,7 +704,9 @@ pub const DeclGen = struct {
@intCast(c_uint, llvm_params.len), @intCast(c_uint, llvm_params.len),
llvm.Bool.fromBool(is_var_args), 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, .ComptimeInt => unreachable,
.ComptimeFloat => unreachable, .ComptimeFloat => unreachable,
@ -753,7 +774,7 @@ pub const DeclGen = struct {
.Pointer => switch (tv.val.tag()) { .Pointer => switch (tv.val.tag()) {
.decl_ref => { .decl_ref => {
if (tv.ty.isSlice()) { if (tv.ty.isSlice()) {
var buf: Type.Payload.ElemType = undefined; var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const ptr_ty = tv.ty.slicePtrFieldType(&buf); const ptr_ty = tv.ty.slicePtrFieldType(&buf);
var slice_len: Value.Payload.U64 = .{ var slice_len: Value.Payload.U64 = .{
.base = .{ .tag = .int_u64 }, .base = .{ .tag = .int_u64 },
@ -783,12 +804,13 @@ pub const DeclGen = struct {
decl.alive = true; decl.alive = true;
const val = try self.resolveGlobalDecl(decl); const val = try self.resolveGlobalDecl(decl);
const llvm_var_type = try self.llvmType(tv.ty); 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); return val.constBitCast(llvm_type);
}, },
.slice => { .slice => {
const slice = tv.val.castTag(.slice).?.data; const slice = tv.val.castTag(.slice).?.data;
var buf: Type.Payload.ElemType = undefined; var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const fields: [2]*const llvm.Value = .{ const fields: [2]*const llvm.Value = .{
try self.genTypedValue(.{ try self.genTypedValue(.{
.ty = tv.ty.slicePtrFieldType(&buf), .ty = tv.ty.slicePtrFieldType(&buf),

View File

@ -197,6 +197,9 @@ pub const Module = opaque {
pub const addFunction = LLVMAddFunction; pub const addFunction = LLVMAddFunction;
extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value; 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; pub const getNamedFunction = LLVMGetNamedFunction;
extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value; extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value;
@ -209,6 +212,9 @@ pub const Module = opaque {
pub const addGlobal = LLVMAddGlobal; pub const addGlobal = LLVMAddGlobal;
extern fn LLVMAddGlobal(M: *const Module, Ty: *const Type, Name: [*:0]const u8) *const Value; 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; pub const getNamedGlobal = LLVMGetNamedGlobal;
extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value; extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value;
@ -1005,3 +1011,65 @@ pub const TypeKind = enum(c_int) {
BFloat, BFloat,
X86_AMX, 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;
};
};

View File

@ -1147,7 +1147,7 @@ const Writer = struct {
cur_bit_bag >>= 1; cur_bit_bag >>= 1;
const has_align = @truncate(u1, cur_bit_bag) != 0; const has_align = @truncate(u1, cur_bit_bag) != 0;
cur_bit_bag >>= 1; 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; cur_bit_bag >>= 1;
const sub_index = extra_index; const sub_index = extra_index;
@ -1165,7 +1165,12 @@ const Writer = struct {
extra_index += 1; extra_index += 1;
break :inst inst; 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]); const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
extra_index += 1; extra_index += 1;
break :inst inst; break :inst inst;
@ -1196,6 +1201,11 @@ const Writer = struct {
try self.writeInstRef(stream, align_inst); try self.writeInstRef(stream, align_inst);
try stream.writeAll(")"); try stream.writeAll(")");
} }
if (addrspace_inst != .none) {
try stream.writeAll(" addrspace(");
try self.writeInstRef(stream, addrspace_inst);
try stream.writeAll(")");
}
if (section_inst != .none) { if (section_inst != .none) {
try stream.writeAll(" linksection("); try stream.writeAll(" linksection(");
try self.writeInstRef(stream, section_inst); try self.writeInstRef(stream, section_inst);

View File

@ -86,6 +86,14 @@ enum CallingConvention {
CallingConventionSysV CallingConventionSysV
}; };
// Stage 1 supports only the generic address space
enum AddressSpace {
AddressSpaceGeneric,
AddressSpaceGS,
AddressSpaceFS,
AddressSpaceSS,
};
// This one corresponds to the builtin.zig enum. // This one corresponds to the builtin.zig enum.
enum BuiltinPtrSize { enum BuiltinPtrSize {
BuiltinPtrSizeOne, BuiltinPtrSizeOne,

View File

@ -1019,6 +1019,16 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
zig_unreachable(); 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) { ZigType *get_stack_trace_type(CodeGen *g) {
if (g->stack_trace_type == nullptr) { if (g->stack_trace_type == nullptr) {
g->stack_trace_type = get_builtin_type(g, "StackTrace"); g->stack_trace_type = get_builtin_type(g, "StackTrace");

View File

@ -242,6 +242,8 @@ Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result);
bool calling_convention_allows_zig_types(CallingConvention cc); bool calling_convention_allows_zig_types(CallingConvention cc);
const char *calling_convention_name(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); 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); void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);

View File

@ -17904,7 +17904,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, Scope *scope, AstNode
result->special = ConstValSpecialStatic; result->special = ConstValSpecialStatic;
result->type = type_info_pointer_type; 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; result->data.x_struct.fields = fields;
// size: Size // 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->base.id = LazyValueIdAlignOf;
lazy_align_of->target_type = ir_const_type(ira, scope, source_node, attrs_type->data.pointer.child_type); lazy_align_of->target_type = ir_const_type(ira, scope, source_node, attrs_type->data.pointer.child_type);
} }
// child: type // address_space: AddressSpace,
ensure_field_index(result->type, "child", 4); ensure_field_index(result->type, "address_space", 4);
fields[4]->special = ConstValSpecialStatic; fields[4]->special = ConstValSpecialStatic;
fields[4]->type = ira->codegen->builtin_types.entry_type; fields[4]->type = get_builtin_type(ira->codegen, "AddressSpace");
fields[4]->data.x_type = attrs_type->data.pointer.child_type; bigint_init_unsigned(&fields[4]->data.x_enum_tag, AddressSpaceGeneric);
// is_allowzero: bool // child: type
ensure_field_index(result->type, "is_allowzero", 5); ensure_field_index(result->type, "child", 5);
fields[5]->special = ConstValSpecialStatic; fields[5]->special = ConstValSpecialStatic;
fields[5]->type = ira->codegen->builtin_types.entry_bool; fields[5]->type = ira->codegen->builtin_types.entry_type;
fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero; fields[5]->data.x_type = attrs_type->data.pointer.child_type;
// sentinel: anytype // is_allowzero: bool
ensure_field_index(result->type, "sentinel", 6); ensure_field_index(result->type, "is_allowzero", 6);
fields[6]->special = ConstValSpecialStatic; 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) { if (attrs_type->data.pointer.sentinel != nullptr) {
fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type); fields[7]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
set_optional_payload(fields[6], attrs_type->data.pointer.sentinel); set_optional_payload(fields[7], attrs_type->data.pointer.sentinel);
} else { } else {
fields[6]->type = ira->codegen->builtin_types.entry_null; fields[7]->type = ira->codegen->builtin_types.entry_null;
} }
return result; return result;
@ -18465,7 +18470,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour
result->special = ConstValSpecialStatic; result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Fn", nullptr); 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; result->data.x_struct.fields = fields;
// calling_convention: TypeInfo.CallingConvention // 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)); 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); 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); 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)) if (type_is_invalid(elem_type))
return ira->codegen->invalid_inst_gen->value->type; return ira->codegen->invalid_inst_gen->value->type;
ZigValue *sentinel; 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))) elem_type, &sentinel)))
{ {
return ira->codegen->invalid_inst_gen->value->type; 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) if (alignment == nullptr)
return ira->codegen->invalid_inst_gen->value->type; 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; bool is_const;
if ((err = get_const_field_bool(ira, source_node, payload, "is_const", 1, &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; 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; 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))) &is_allowzero)))
{ {
return ira->codegen->invalid_inst_gen->value->type; return ira->codegen->invalid_inst_gen->value->type;
} }
ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen,
elem_type, elem_type,
is_const, is_const,

View File

@ -544,3 +544,21 @@ pub fn largestAtomicBits(target: std.Target) u32 {
.x86_64 => 128, .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;
}

View File

@ -2614,6 +2614,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex {
.type_node = type_node, .type_node = type_node,
.align_node = align_node, .align_node = align_node,
.section_node = section_node, .section_node = section_node,
.addrspace_node = 0,
}), }),
.rhs = init_node, .rhs = init_node,
}, },
@ -2705,6 +2706,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
.lhs = try c.addExtra(std.zig.Ast.Node.FnProtoOne{ .lhs = try c.addExtra(std.zig.Ast.Node.FnProtoOne{
.param = params.items[0], .param = params.items[0],
.align_expr = align_expr, .align_expr = align_expr,
.addrspace_expr = 0, // TODO
.section_expr = section_expr, .section_expr = section_expr,
.callconv_expr = callconv_expr, .callconv_expr = callconv_expr,
}), }),
@ -2720,6 +2722,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
.params_start = span.start, .params_start = span.start,
.params_end = span.end, .params_end = span.end,
.align_expr = align_expr, .align_expr = align_expr,
.addrspace_expr = 0, // TODO
.section_expr = section_expr, .section_expr = section_expr,
.callconv_expr = callconv_expr, .callconv_expr = callconv_expr,
}), }),

View File

@ -127,6 +127,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
=> return .Enum, => return .Enum,
@ -288,6 +289,7 @@ pub const Type = extern union {
.pointee_type = Type.initTag(.comptime_int), .pointee_type = Type.initTag(.comptime_int),
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -299,6 +301,7 @@ pub const Type = extern union {
.pointee_type = Type.initTag(.u8), .pointee_type = Type.initTag(.u8),
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -310,6 +313,7 @@ pub const Type = extern union {
.pointee_type = self.castPointer().?.data, .pointee_type = self.castPointer().?.data,
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -321,6 +325,7 @@ pub const Type = extern union {
.pointee_type = self.castPointer().?.data, .pointee_type = self.castPointer().?.data,
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -332,6 +337,7 @@ pub const Type = extern union {
.pointee_type = self.castPointer().?.data, .pointee_type = self.castPointer().?.data,
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -343,6 +349,7 @@ pub const Type = extern union {
.pointee_type = Type.initTag(.u8), .pointee_type = Type.initTag(.u8),
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -354,6 +361,7 @@ pub const Type = extern union {
.pointee_type = self.castPointer().?.data, .pointee_type = self.castPointer().?.data,
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -365,6 +373,7 @@ pub const Type = extern union {
.pointee_type = Type.initTag(.u8), .pointee_type = Type.initTag(.u8),
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -376,6 +385,7 @@ pub const Type = extern union {
.pointee_type = self.castPointer().?.data, .pointee_type = self.castPointer().?.data,
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -387,6 +397,7 @@ pub const Type = extern union {
.pointee_type = self.castPointer().?.data, .pointee_type = self.castPointer().?.data,
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -398,6 +409,7 @@ pub const Type = extern union {
.pointee_type = self.castPointer().?.data, .pointee_type = self.castPointer().?.data,
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -409,6 +421,7 @@ pub const Type = extern union {
.pointee_type = self.castPointer().?.data, .pointee_type = self.castPointer().?.data,
.sentinel = null, .sentinel = null,
.@"align" = 0, .@"align" = 0,
.@"addrspace" = .generic,
.bit_offset = 0, .bit_offset = 0,
.host_size = 0, .host_size = 0,
.@"allowzero" = false, .@"allowzero" = false,
@ -461,6 +474,8 @@ pub const Type = extern union {
return false; return false;
if (info_a.host_size != info_b.host_size) if (info_a.host_size != info_b.host_size)
return false; return false;
if (info_a.@"addrspace" != info_b.@"addrspace")
return false;
const sentinel_a = info_a.sentinel; const sentinel_a = info_a.sentinel;
const sentinel_b = info_b.sentinel; const sentinel_b = info_b.sentinel;
@ -746,6 +761,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -835,6 +851,7 @@ pub const Type = extern union {
.pointee_type = try payload.pointee_type.copy(allocator), .pointee_type = try payload.pointee_type.copy(allocator),
.sentinel = sent, .sentinel = sent,
.@"align" = payload.@"align", .@"align" = payload.@"align",
.@"addrspace" = payload.@"addrspace",
.bit_offset = payload.bit_offset, .bit_offset = payload.bit_offset,
.host_size = payload.host_size, .host_size = payload.host_size,
.@"allowzero" = payload.@"allowzero", .@"allowzero" = payload.@"allowzero",
@ -958,6 +975,7 @@ pub const Type = extern union {
.atomic_order => return writer.writeAll("std.builtin.AtomicOrder"), .atomic_order => return writer.writeAll("std.builtin.AtomicOrder"),
.atomic_rmw_op => return writer.writeAll("std.builtin.AtomicRmwOp"), .atomic_rmw_op => return writer.writeAll("std.builtin.AtomicRmwOp"),
.calling_convention => return writer.writeAll("std.builtin.CallingConvention"), .calling_convention => return writer.writeAll("std.builtin.CallingConvention"),
.address_space => return writer.writeAll("std.builtin.AddressSpace"),
.float_mode => return writer.writeAll("std.builtin.FloatMode"), .float_mode => return writer.writeAll("std.builtin.FloatMode"),
.reduce_op => return writer.writeAll("std.builtin.ReduceOp"), .reduce_op => return writer.writeAll("std.builtin.ReduceOp"),
.call_options => return writer.writeAll("std.builtin.CallOptions"), .call_options => return writer.writeAll("std.builtin.CallOptions"),
@ -1111,6 +1129,9 @@ pub const Type = extern union {
} }
try writer.writeAll(") "); try writer.writeAll(") ");
} }
if (payload.@"addrspace" != .generic) {
try writer.print("addrspace(.{s}) ", .{@tagName(payload.@"addrspace")});
}
if (!payload.mutable) try writer.writeAll("const "); if (!payload.mutable) try writer.writeAll("const ");
if (payload.@"volatile") try writer.writeAll("volatile "); if (payload.@"volatile") try writer.writeAll("volatile ");
if (payload.@"allowzero") try writer.writeAll("allowzero "); if (payload.@"allowzero") try writer.writeAll("allowzero ");
@ -1186,6 +1207,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -1301,6 +1323,7 @@ pub const Type = extern union {
.atomic_order => return Value.initTag(.atomic_order_type), .atomic_order => return Value.initTag(.atomic_order_type),
.atomic_rmw_op => return Value.initTag(.atomic_rmw_op_type), .atomic_rmw_op => return Value.initTag(.atomic_rmw_op_type),
.calling_convention => return Value.initTag(.calling_convention_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), .float_mode => return Value.initTag(.float_mode_type),
.reduce_op => return Value.initTag(.reduce_op_type), .reduce_op => return Value.initTag(.reduce_op_type),
.call_options => return Value.initTag(.call_options_type), .call_options => return Value.initTag(.call_options_type),
@ -1362,6 +1385,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .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. /// Asserts that hasCodeGenBits() is true.
pub fn abiAlignment(self: Type, target: Target) u32 { pub fn abiAlignment(self: Type, target: Target) u32 {
return switch (self.tag()) { return switch (self.tag()) {
@ -1508,6 +1556,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -1734,6 +1783,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -2019,6 +2069,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .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()) { switch (self.tag()) {
.const_slice_u8 => return Type.initTag(.manyptr_const_u8), .const_slice_u8 => return Type.initTag(.manyptr_const_u8),
.const_slice => { .const_slice => {
const elem_type = self.castTag(.const_slice).?.data; const elem_type = self.castTag(.const_slice).?.data;
buffer.* = .{ buffer.* = .{
.elem_type = .{
.base = .{ .tag = .many_const_pointer }, .base = .{ .tag = .many_const_pointer },
.data = elem_type, .data = elem_type,
},
}; };
return Type.initPayload(&buffer.base); return Type.initPayload(&buffer.elem_type.base);
}, },
.mut_slice => { .mut_slice => {
const elem_type = self.castTag(.mut_slice).?.data; const elem_type = self.castTag(.mut_slice).?.data;
buffer.* = .{ buffer.* = .{
.elem_type = .{
.base = .{ .tag = .many_mut_pointer }, .base = .{ .tag = .many_mut_pointer },
.data = elem_type, .data = elem_type,
},
}; };
return Type.initPayload(&buffer.base); return Type.initPayload(&buffer.elem_type.base);
}, },
.pointer => { .pointer => {
const payload = self.castTag(.pointer).?.data; const payload = self.castTag(.pointer).?.data;
assert(payload.size == .Slice); 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.* = .{ buffer.* = .{
.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 }, .base = .{ .tag = .many_mut_pointer },
.data = payload.pointee_type, .data = payload.pointee_type,
},
}; };
return Type.initPayload(&buffer.elem_type.base);
} else { } else {
buffer.* = .{ buffer.* = .{
.elem_type = .{
.base = .{ .tag = .many_const_pointer }, .base = .{ .tag = .many_const_pointer },
.data = payload.pointee_type, .data = payload.pointee_type,
},
}; };
return Type.initPayload(&buffer.elem_type.base);
} }
return Type.initPayload(&buffer.base);
}, },
else => unreachable, else => unreachable,
@ -2793,6 +2884,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -3000,6 +3092,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -3024,6 +3117,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -3047,6 +3141,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -3100,6 +3195,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -3155,6 +3251,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -3192,6 +3289,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -3242,6 +3340,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -3302,6 +3401,7 @@ pub const Type = extern union {
atomic_order, atomic_order,
atomic_rmw_op, atomic_rmw_op,
calling_convention, calling_convention,
address_space,
float_mode, float_mode,
reduce_op, reduce_op,
call_options, call_options,
@ -3425,6 +3525,7 @@ pub const Type = extern union {
.atomic_order, .atomic_order,
.atomic_rmw_op, .atomic_rmw_op,
.calling_convention, .calling_convention,
.address_space,
.float_mode, .float_mode,
.reduce_op, .reduce_op,
.call_options, .call_options,
@ -3580,6 +3681,7 @@ pub const Type = extern union {
sentinel: ?Value, sentinel: ?Value,
/// If zero use pointee_type.AbiAlign() /// If zero use pointee_type.AbiAlign()
@"align": u32, @"align": u32,
@"addrspace": std.builtin.AddressSpace,
bit_offset: u16, bit_offset: u16,
host_size: u16, host_size: u16,
@"allowzero": bool, @"allowzero": bool,

View File

@ -63,6 +63,7 @@ pub const Value = extern union {
atomic_order_type, atomic_order_type,
atomic_rmw_op_type, atomic_rmw_op_type,
calling_convention_type, calling_convention_type,
address_space_type,
float_mode_type, float_mode_type,
reduce_op_type, reduce_op_type,
call_options_type, call_options_type,
@ -226,6 +227,7 @@ pub const Value = extern union {
.atomic_order_type, .atomic_order_type,
.atomic_rmw_op_type, .atomic_rmw_op_type,
.calling_convention_type, .calling_convention_type,
.address_space_type,
.float_mode_type, .float_mode_type,
.reduce_op_type, .reduce_op_type,
.call_options_type, .call_options_type,
@ -412,6 +414,7 @@ pub const Value = extern union {
.atomic_order_type, .atomic_order_type,
.atomic_rmw_op_type, .atomic_rmw_op_type,
.calling_convention_type, .calling_convention_type,
.address_space_type,
.float_mode_type, .float_mode_type,
.reduce_op_type, .reduce_op_type,
.call_options_type, .call_options_type,
@ -625,6 +628,7 @@ pub const Value = extern union {
.atomic_order_type => return out_stream.writeAll("std.builtin.AtomicOrder"), .atomic_order_type => return out_stream.writeAll("std.builtin.AtomicOrder"),
.atomic_rmw_op_type => return out_stream.writeAll("std.builtin.AtomicRmwOp"), .atomic_rmw_op_type => return out_stream.writeAll("std.builtin.AtomicRmwOp"),
.calling_convention_type => return out_stream.writeAll("std.builtin.CallingConvention"), .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"), .float_mode_type => return out_stream.writeAll("std.builtin.FloatMode"),
.reduce_op_type => return out_stream.writeAll("std.builtin.ReduceOp"), .reduce_op_type => return out_stream.writeAll("std.builtin.ReduceOp"),
.call_options_type => return out_stream.writeAll("std.builtin.CallOptions"), .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_order_type => Type.initTag(.atomic_order),
.atomic_rmw_op_type => Type.initTag(.atomic_rmw_op), .atomic_rmw_op_type => Type.initTag(.atomic_rmw_op),
.calling_convention_type => Type.initTag(.calling_convention), .calling_convention_type => Type.initTag(.calling_convention),
.address_space_type => Type.initTag(.address_space),
.float_mode_type => Type.initTag(.float_mode), .float_mode_type => Type.initTag(.float_mode),
.reduce_op_type => Type.initTag(.reduce_op), .reduce_op_type => Type.initTag(.reduce_op),
.call_options_type => Type.initTag(.call_options), .call_options_type => Type.initTag(.call_options),

View File

@ -416,6 +416,11 @@ ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref) {
return wrap(Type::getTokenTy(*unwrap(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, LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
unsigned NumArgs, ZigLLVM_CallingConv CC, ZigLLVM_CallAttr attr, const char *Name) unsigned NumArgs, ZigLLVM_CallingConv CC, ZigLLVM_CallAttr attr, const char *Name)
{ {

View File

@ -65,6 +65,9 @@ ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, co
ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref); 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 { enum ZigLLVM_CallingConv {
ZigLLVM_C = 0, ZigLLVM_C = 0,
ZigLLVM_Fast = 8, ZigLLVM_Fast = 8,

View File

@ -137,6 +137,7 @@ test "@Type create slice with null sentinel" {
.is_volatile = false, .is_volatile = false,
.is_allowzero = false, .is_allowzero = false,
.alignment = 8, .alignment = 8,
.address_space = .generic,
.child = *i32, .child = *i32,
.sentinel = null, .sentinel = null,
}, },

View File

@ -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'",
});
}
} }

View File

@ -711,6 +711,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ .is_const = false, \\ .is_const = false,
\\ .is_volatile = false, \\ .is_volatile = false,
\\ .alignment = 1, \\ .alignment = 1,
\\ .address_space = .generic,
\\ .child = u8, \\ .child = u8,
\\ .is_allowzero = false, \\ .is_allowzero = false,
\\ .sentinel = 0, \\ .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", "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", ctx.testErrStage1("helpful return type error message",
\\export fn foo() u32 { \\export fn foo() u32 {
\\ return error.Ohno; \\ return error.Ohno;

View File

@ -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;
\\}
, "");
}
} }