Commit 329a359974

Andrew Kelley <andrew@ziglang.org>
2021-04-23 04:21:33
AstGen: implement align and linksection on globals
1 parent 507a809
Changed files (2)
src/AstGen.zig
@@ -2482,11 +2482,14 @@ const WipDecls = struct {
     decl_index: usize = 0,
     cur_bit_bag: u32 = 0,
     bit_bag: ArrayListUnmanaged(u32) = .{},
-    name_and_value: ArrayListUnmanaged(u32) = .{},
+    payload: ArrayListUnmanaged(u32) = .{},
+
+    const bits_per_field = 4;
+    const fields_per_u32 = 32 / bits_per_field;
 
     fn deinit(wip_decls: *WipDecls, gpa: *Allocator) void {
         wip_decls.bit_bag.deinit(gpa);
-        wip_decls.name_and_value.deinit(gpa);
+        wip_decls.payload.deinit(gpa);
     }
 };
 
@@ -2501,6 +2504,15 @@ fn fnDecl(
     const tree = &astgen.file.tree;
     const token_tags = tree.tokens.items(.tag);
 
+    var decl_gz: GenZir = .{
+        .force_comptime = true,
+        .decl_node_index = fn_proto.ast.proto_node,
+        .parent = &gz.base,
+        .astgen = astgen,
+        .ref_start_index = @intCast(u32, Zir.Inst.Ref.typed_value_map.len),
+    };
+    defer decl_gz.instructions.deinit(gpa);
+
     const is_pub = fn_proto.visib_token != null;
     const is_export = blk: {
         const maybe_export_token = fn_proto.extern_export_token orelse break :blk false;
@@ -2510,13 +2522,22 @@ fn fnDecl(
         const maybe_extern_token = fn_proto.extern_export_token orelse break :blk false;
         break :blk token_tags[maybe_extern_token] == .keyword_extern;
     };
-    if (wip_decls.decl_index % 16 == 0 and wip_decls.decl_index != 0) {
+    const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
+        break :inst try expr(&decl_gz, &decl_gz.base, align_rl, fn_proto.ast.align_expr);
+    };
+    const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
+        break :inst try comptimeExpr(&decl_gz, &decl_gz.base, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr);
+    };
+
+    if (wip_decls.decl_index % WipDecls.fields_per_u32 == 0 and wip_decls.decl_index != 0) {
         try wip_decls.bit_bag.append(gpa, wip_decls.cur_bit_bag);
         wip_decls.cur_bit_bag = 0;
     }
-    wip_decls.cur_bit_bag = (wip_decls.cur_bit_bag >> 2) |
-        (@as(u32, @boolToInt(is_pub)) << 30) |
-        (@as(u32, @boolToInt(is_export)) << 31);
+    wip_decls.cur_bit_bag = (wip_decls.cur_bit_bag >> WipDecls.bits_per_field) |
+        (@as(u32, @boolToInt(is_pub)) << 28) |
+        (@as(u32, @boolToInt(is_export)) << 29) |
+        (@as(u32, @boolToInt(align_inst != .none)) << 30) |
+        (@as(u32, @boolToInt(section_inst != .none)) << 31);
     wip_decls.decl_index += 1;
 
     // The AST params array does not contain anytype and ... parameters.
@@ -2537,15 +2558,6 @@ fn fnDecl(
     const param_types = try gpa.alloc(Zir.Inst.Ref, param_count);
     defer gpa.free(param_types);
 
-    var decl_gz: GenZir = .{
-        .force_comptime = true,
-        .decl_node_index = fn_proto.ast.proto_node,
-        .parent = &gz.base,
-        .astgen = astgen,
-        .ref_start_index = @intCast(u32, Zir.Inst.Ref.typed_value_map.len),
-    };
-    defer decl_gz.instructions.deinit(gpa);
-
     var is_var_args = false;
     {
         var param_type_i: usize = 0;
@@ -2577,21 +2589,6 @@ fn fnDecl(
         break :blk lib_name_str.index;
     } else 0;
 
-    if (fn_proto.ast.align_expr != 0) {
-        return astgen.failNode(
-            fn_proto.ast.align_expr,
-            "TODO implement function align expression",
-            .{},
-        );
-    }
-    if (fn_proto.ast.section_expr != 0) {
-        return astgen.failNode(
-            fn_proto.ast.section_expr,
-            "TODO implement function section expression",
-            .{},
-        );
-    }
-
     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
     const is_inferred_error = token_tags[maybe_bang] == .bang;
 
@@ -2713,9 +2710,15 @@ fn fnDecl(
     _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
     try decl_gz.setBlockBody(block_inst);
 
-    try wip_decls.name_and_value.ensureCapacity(gpa, wip_decls.name_and_value.items.len + 2);
-    wip_decls.name_and_value.appendAssumeCapacity(fn_name_str_index);
-    wip_decls.name_and_value.appendAssumeCapacity(block_inst);
+    try wip_decls.payload.ensureUnusedCapacity(gpa, 4);
+    wip_decls.payload.appendAssumeCapacity(fn_name_str_index);
+    wip_decls.payload.appendAssumeCapacity(block_inst);
+    if (align_inst != .none) {
+        wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));
+    }
+    if (section_inst != .none) {
+        wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst));
+    }
 }
 
 fn globalVarDecl(
@@ -2730,6 +2733,14 @@ fn globalVarDecl(
     const tree = &astgen.file.tree;
     const token_tags = tree.tokens.items(.tag);
 
+    var block_scope: GenZir = .{
+        .parent = scope,
+        .decl_node_index = node,
+        .astgen = astgen,
+        .force_comptime = true,
+    };
+    defer block_scope.instructions.deinit(gpa);
+
     const is_pub = var_decl.visib_token != null;
     const is_export = blk: {
         const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
@@ -2739,13 +2750,21 @@ fn globalVarDecl(
         const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
         break :blk token_tags[maybe_extern_token] == .keyword_extern;
     };
-    if (wip_decls.decl_index % 16 == 0 and wip_decls.decl_index != 0) {
+    const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: {
+        break :inst try expr(&block_scope, &block_scope.base, align_rl, var_decl.ast.align_node);
+    };
+    const 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);
+    };
+    if (wip_decls.decl_index % WipDecls.fields_per_u32 == 0 and wip_decls.decl_index != 0) {
         try wip_decls.bit_bag.append(gpa, wip_decls.cur_bit_bag);
         wip_decls.cur_bit_bag = 0;
     }
-    wip_decls.cur_bit_bag = (wip_decls.cur_bit_bag >> 2) |
-        (@as(u32, @boolToInt(is_pub)) << 30) |
-        (@as(u32, @boolToInt(is_export)) << 31);
+    wip_decls.cur_bit_bag = (wip_decls.cur_bit_bag >> WipDecls.bits_per_field) |
+        (@as(u32, @boolToInt(is_pub)) << 28) |
+        (@as(u32, @boolToInt(is_export)) << 29) |
+        (@as(u32, @boolToInt(align_inst != .none)) << 30) |
+        (@as(u32, @boolToInt(section_inst != .none)) << 31);
     wip_decls.decl_index += 1;
 
     const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
@@ -2762,12 +2781,6 @@ fn globalVarDecl(
     } else 0;
 
     assert(var_decl.comptime_token == null); // handled by parser
-    if (var_decl.ast.align_node != 0) {
-        return astgen.failNode(var_decl.ast.align_node, "TODO implement alignment on globals", .{});
-    }
-    if (var_decl.ast.section_node != 0) {
-        return astgen.failNode(var_decl.ast.section_node, "TODO linksection on globals", .{});
-    }
 
     const var_inst: Zir.Inst.Index = if (var_decl.ast.init_node != 0) vi: {
         if (is_extern) {
@@ -2778,14 +2791,6 @@ fn globalVarDecl(
             );
         }
 
-        var block_scope: GenZir = .{
-            .parent = scope,
-            .decl_node_index = node,
-            .astgen = astgen,
-            .force_comptime = true,
-        };
-        defer block_scope.instructions.deinit(gpa);
-
         const init_result_loc: AstGen.ResultLoc = if (var_decl.ast.type_node != 0) .{
             .ty = try expr(
                 &block_scope,
@@ -2812,7 +2817,7 @@ fn globalVarDecl(
     } else if (var_decl.ast.type_node != 0) {
         // Extern variable which has an explicit type.
 
-        const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
+        const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node);
 
         return astgen.failNode(node, "TODO AstGen extern global variable", .{});
     } else {
@@ -2822,9 +2827,15 @@ fn globalVarDecl(
     const name_token = var_decl.ast.mut_token + 1;
     const name_str_index = try gz.identAsString(name_token);
 
-    try wip_decls.name_and_value.ensureCapacity(gpa, wip_decls.name_and_value.items.len + 2);
-    wip_decls.name_and_value.appendAssumeCapacity(name_str_index);
-    wip_decls.name_and_value.appendAssumeCapacity(var_inst);
+    try wip_decls.payload.ensureUnusedCapacity(gpa, 4);
+    wip_decls.payload.appendAssumeCapacity(name_str_index);
+    wip_decls.payload.appendAssumeCapacity(var_inst);
+    if (align_inst != .none) {
+        wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));
+    }
+    if (section_inst != .none) {
+        wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst));
+    }
 }
 
 fn comptimeDecl(
@@ -3080,7 +3091,7 @@ fn structDeclInner(
             (@as(u32, @boolToInt(have_value)) << 31);
 
         if (have_align) {
-            const align_inst = try expr(&block_scope, &block_scope.base, .{ .ty = .u32_type }, member.ast.align_expr);
+            const align_inst = try expr(&block_scope, &block_scope.base, align_rl, member.ast.align_expr);
             fields_data.appendAssumeCapacity(@enumToInt(align_inst));
         }
         if (have_value) {
@@ -3097,9 +3108,9 @@ fn structDeclInner(
         }
     }
     {
-        const empty_slot_count = 16 - (wip_decls.decl_index % 16);
-        if (empty_slot_count < 16) {
-            wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
+        const empty_slot_count = WipDecls.fields_per_u32 - (wip_decls.decl_index % WipDecls.fields_per_u32);
+        if (empty_slot_count < WipDecls.fields_per_u32) {
+            wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field);
         }
     }
 
@@ -3113,7 +3124,7 @@ fn structDeclInner(
         bit_bag.items.len + @boolToInt(field_index != 0) + fields_data.items.len +
         block_scope.instructions.items.len +
         wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +
-        wip_decls.name_and_value.items.len);
+        wip_decls.payload.items.len);
     const zir_datas = astgen.instructions.items(.data);
     zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{
         .body_len = @intCast(u32, block_scope.instructions.items.len),
@@ -3132,7 +3143,7 @@ fn structDeclInner(
     if (wip_decls.decl_index != 0) {
         astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
     }
-    astgen.extra.appendSliceAssumeCapacity(wip_decls.name_and_value.items);
+    astgen.extra.appendSliceAssumeCapacity(wip_decls.payload.items);
 
     return gz.indexToRef(decl_inst);
 }
@@ -3321,9 +3332,9 @@ fn unionDeclInner(
         }
     }
     {
-        const empty_slot_count = 16 - (wip_decls.decl_index % 16);
-        if (empty_slot_count < 16) {
-            wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
+        const empty_slot_count = WipDecls.fields_per_u32 - (wip_decls.decl_index % WipDecls.fields_per_u32);
+        if (empty_slot_count < WipDecls.fields_per_u32) {
+            wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field);
         }
     }
 
@@ -3337,7 +3348,7 @@ fn unionDeclInner(
         bit_bag.items.len + 1 + fields_data.items.len +
         block_scope.instructions.items.len +
         wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +
-        wip_decls.name_and_value.items.len);
+        wip_decls.payload.items.len);
     const zir_datas = astgen.instructions.items(.data);
     zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{
         .tag_type = arg_inst,
@@ -3355,7 +3366,7 @@ fn unionDeclInner(
     if (wip_decls.decl_index != 0) {
         astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
     }
-    astgen.extra.appendSliceAssumeCapacity(wip_decls.name_and_value.items);
+    astgen.extra.appendSliceAssumeCapacity(wip_decls.payload.items);
 
     return gz.indexToRef(decl_inst);
 }
@@ -3653,9 +3664,9 @@ fn containerDecl(
                 }
             }
             {
-                const empty_slot_count = 16 - (wip_decls.decl_index % 16);
-                if (empty_slot_count < 16) {
-                    wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
+                const empty_slot_count = WipDecls.fields_per_u32 - (wip_decls.decl_index % WipDecls.fields_per_u32);
+                if (empty_slot_count < WipDecls.fields_per_u32) {
+                    wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field);
                 }
             }
 
@@ -3669,7 +3680,7 @@ fn containerDecl(
                 bit_bag.items.len + 1 + fields_data.items.len +
                 block_scope.instructions.items.len +
                 wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +
-                wip_decls.name_and_value.items.len);
+                wip_decls.payload.items.len);
             const zir_datas = astgen.instructions.items(.data);
             zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{
                 .tag_type = arg_inst,
@@ -3686,7 +3697,7 @@ fn containerDecl(
             if (wip_decls.decl_index != 0) {
                 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
             }
-            astgen.extra.appendSliceAssumeCapacity(wip_decls.name_and_value.items);
+            astgen.extra.appendSliceAssumeCapacity(wip_decls.payload.items);
 
             return rvalue(gz, scope, rl, gz.indexToRef(decl_inst), node);
         },
src/Zir.zig
@@ -2021,13 +2021,17 @@ pub const Inst = struct {
     ///        align: Ref, // if corresponding bit is set
     ///        default_value: Ref, // if corresponding bit is set
     ///    }
-    /// 3. decl_bits: u32 // for every 16 decls
-    ///    - sets of 2 bits:
-    ///      0b0X: whether corresponding decl is pub
-    ///      0bX0: whether corresponding decl is exported
+    /// 3. decl_bits: u32 // for every 8 decls
+    ///    - sets of 4 bits:
+    ///      0b000X: whether corresponding decl is pub
+    ///      0b00X0: whether corresponding decl is exported
+    ///      0b0X00: whether corresponding decl has an align expression
+    ///      0bX000: whether corresponding decl has a linksection expression
     /// 4. decl: { // for every decls_len
     ///        name: u32, // null terminated string index
     ///        value: Index,
+    ///        align: Ref, // if corresponding bit is set
+    ///        link_section: Ref, // if corresponding bit is set
     ///    }
     pub const StructDecl = struct {
         body_len: u32,
@@ -2043,13 +2047,17 @@ pub const Inst = struct {
     ///        field_name: u32,
     ///        value: Ref, // if corresponding bit is set
     ///    }
-    /// 3. decl_bits: u32 // for every 16 decls
-    ///    - sets of 2 bits:
-    ///      0b0X: whether corresponding decl is pub
-    ///      0bX0: whether corresponding decl is exported
+    /// 3. decl_bits: u32 // for every 8 decls
+    ///    - sets of 4 bits:
+    ///      0b000X: whether corresponding decl is pub
+    ///      0b00X0: whether corresponding decl is exported
+    ///      0b0X00: whether corresponding decl has an align expression
+    ///      0bX000: whether corresponding decl has a linksection expression
     /// 4. decl: { // for every decls_len
     ///        name: u32, // null terminated string index
     ///        value: Index,
+    ///        align: Ref, // if corresponding bit is set
+    ///        link_section: Ref, // if corresponding bit is set
     ///    }
     pub const EnumDecl = struct {
         /// Can be `Ref.none`.
@@ -2077,13 +2085,17 @@ pub const Inst = struct {
     ///        align: Ref, // if corresponding bit is set
     ///        tag_value: Ref, // if corresponding bit is set
     ///    }
-    /// 3. decl_bits: u32 // for every 16 decls
-    ///    - sets of 2 bits:
-    ///      0b0X: whether corresponding decl is pub
-    ///      0bX0: whether corresponding decl is exported
+    /// 3. decl_bits: u32 // for every 8 decls
+    ///    - sets of 4 bits:
+    ///      0b000X: whether corresponding decl is pub
+    ///      0b00X0: whether corresponding decl is exported
+    ///      0b0X00: whether corresponding decl has an align expression
+    ///      0bX000: whether corresponding decl has a linksection expression
     /// 4. decl: { // for every decls_len
     ///        name: u32, // null terminated string index
     ///        value: Index,
+    ///        align: Ref, // if corresponding bit is set
+    ///        link_section: Ref, // if corresponding bit is set
     ///    }
     pub const UnionDecl = struct {
         /// Can be `Ref.none`.
@@ -3072,13 +3084,13 @@ const Writer = struct {
 
     fn writeDecls(self: *Writer, stream: anytype, decls_len: u32, extra_start: usize) !void {
         const parent_decl_node = self.parent_decl_node;
-        const bit_bags_count = std.math.divCeil(usize, decls_len, 16) catch unreachable;
+        const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
         var extra_index = extra_start + bit_bags_count;
         var bit_bag_index: usize = extra_start;
         var cur_bit_bag: u32 = undefined;
         var decl_i: u32 = 0;
         while (decl_i < decls_len) : (decl_i += 1) {
-            if (decl_i % 16 == 0) {
+            if (decl_i % 8 == 0) {
                 cur_bit_bag = self.code.extra[bit_bag_index];
                 bit_bag_index += 1;
             }
@@ -3086,19 +3098,44 @@ const Writer = struct {
             cur_bit_bag >>= 1;
             const is_exported = @truncate(u1, cur_bit_bag) != 0;
             cur_bit_bag >>= 1;
+            const has_align = @truncate(u1, cur_bit_bag) != 0;
+            cur_bit_bag >>= 1;
+            const has_section = @truncate(u1, cur_bit_bag) != 0;
+            cur_bit_bag >>= 1;
 
             const decl_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
             extra_index += 1;
             const decl_index = self.code.extra[extra_index];
             extra_index += 1;
+            const align_inst: Inst.Ref = if (!has_align) .none else inst: {
+                const inst = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
+                extra_index += 1;
+                break :inst inst;
+            };
+            const section_inst: Inst.Ref = if (!has_section) .none else inst: {
+                const inst = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
+                extra_index += 1;
+                break :inst inst;
+            };
 
             const tag = self.code.instructions.items(.tag)[decl_index];
             const pub_str = if (is_pub) "pub " else "";
             const export_str = if (is_exported) "export " else "";
             try stream.writeByteNTimes(' ', self.indent);
-            try stream.print("{s}{s}{}: %{d} = {s}(", .{
-                pub_str, export_str, std.zig.fmtId(decl_name), decl_index, @tagName(tag),
+            try stream.print("{s}{s}{}", .{
+                pub_str, export_str, std.zig.fmtId(decl_name),
             });
+            if (align_inst != .none) {
+                try stream.writeAll(" align(");
+                try self.writeInstRef(stream, align_inst);
+                try stream.writeAll(")");
+            }
+            if (section_inst != .none) {
+                try stream.writeAll(" linksection(");
+                try self.writeInstRef(stream, section_inst);
+                try stream.writeAll(")");
+            }
+            try stream.print(": %{d} = {s}(", .{ decl_index, @tagName(tag) });
 
             const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node;
             const sub_decl_node_off = decl_block_inst_data.src_node;