authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-04 20:38:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-04 20:38:53-07:00
log1b432b557608bd047afaad71ffb7bd2ddf23c444
tree0930ca5f5f1e78460a75dc5d86e243fccb5027ba
parent0bebb688fbc4c6ffb88a2b0aefcf536143bb5f2e

stage2: implement global assembly

So far it's supported by the LLVM backend only. I recommend for the other backends to wait for the resolution of #10761 before adding support for this feature.

5 files changed, 55 insertions(+), 6 deletions(-)

src/Module.zig+15
...@@ -151,6 +151,8 @@ allocated_decls: std.SegmentedList(Decl, 0) = .{},...@@ -151,6 +151,8 @@ allocated_decls: std.SegmentedList(Decl, 0) = .{},
151/// When a Decl object is freed from `allocated_decls`, it is pushed into this stack.151/// When a Decl object is freed from `allocated_decls`, it is pushed into this stack.
152decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{},152decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{},
153153
154global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{},
155
154const MonomorphedFuncsSet = std.HashMapUnmanaged(156const MonomorphedFuncsSet = std.HashMapUnmanaged(
155 *Fn,157 *Fn,
156 void,158 void,
...@@ -2831,6 +2833,7 @@ pub fn deinit(mod: *Module) void {...@@ -2831,6 +2833,7 @@ pub fn deinit(mod: *Module) void {
28312833
2832 mod.decls_free_list.deinit(gpa);2834 mod.decls_free_list.deinit(gpa);
2833 mod.allocated_decls.deinit(gpa);2835 mod.allocated_decls.deinit(gpa);
2836 mod.global_assembly.deinit(gpa);
2834}2837}
28352838
2836pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {2839pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
...@@ -2842,6 +2845,9 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {...@@ -2842,6 +2845,9 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
2842 if (decl.deletion_flag) {2845 if (decl.deletion_flag) {
2843 assert(mod.deletion_set.swapRemove(decl_index));2846 assert(mod.deletion_set.swapRemove(decl_index));
2844 }2847 }
2848 if (mod.global_assembly.fetchRemove(decl_index)) |kv| {
2849 gpa.free(kv.value);
2850 }
2845 if (decl.has_tv) {2851 if (decl.has_tv) {
2846 if (decl.getInnerNamespace()) |namespace| {2852 if (decl.getInnerNamespace()) |namespace| {
2847 namespace.destroyDecls(mod);2853 namespace.destroyDecls(mod);
...@@ -5714,3 +5720,12 @@ pub fn markDeclAlive(mod: *Module, decl: *Decl) void {...@@ -5714,3 +5720,12 @@ pub fn markDeclAlive(mod: *Module, decl: *Decl) void {
5714fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void {5720fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void {
5715 return mod.markDeclAlive(mod.declPtr(decl_index));5721 return mod.markDeclAlive(mod.declPtr(decl_index));
5716}5722}
5723
5724pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void {
5725 try mod.global_assembly.ensureUnusedCapacity(mod.gpa, 1);
5726
5727 const duped_source = try mod.gpa.dupe(u8, source);
5728 errdefer mod.gpa.free(duped_source);
5729
5730 mod.global_assembly.putAssumeCapacityNoClobber(decl_index, duped_source);
5731}
src/Sema.zig+23-5
...@@ -10517,16 +10517,35 @@ fn zirAsm(...@@ -10517,16 +10517,35 @@ fn zirAsm(
10517 const is_volatile = @truncate(u1, extended.small >> 15) != 0;10517 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
10518 const is_global_assembly = sema.func == null;10518 const is_global_assembly = sema.func == null;
1051910519
10520 if (block.is_comptime and !is_global_assembly) {
10521 try sema.requireRuntimeBlock(block, src);
10522 }
10523
10524 if (extra.data.asm_source == 0) {10520 if (extra.data.asm_source == 0) {
10525 // This can move to become an AstGen error after inline assembly improvements land10521 // This can move to become an AstGen error after inline assembly improvements land
10526 // and stage1 code matches stage2 code.10522 // and stage1 code matches stage2 code.
10527 return sema.fail(block, src, "assembly code must use string literal syntax", .{});10523 return sema.fail(block, src, "assembly code must use string literal syntax", .{});
10528 }10524 }
1052910525
10526 const asm_source = sema.code.nullTerminatedString(extra.data.asm_source);
10527
10528 if (is_global_assembly) {
10529 if (outputs_len != 0) {
10530 return sema.fail(block, src, "module-level assembly does not support outputs", .{});
10531 }
10532 if (inputs_len != 0) {
10533 return sema.fail(block, src, "module-level assembly does not support inputs", .{});
10534 }
10535 if (clobbers_len != 0) {
10536 return sema.fail(block, src, "module-level assembly does not support clobbers", .{});
10537 }
10538 if (is_volatile) {
10539 return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{});
10540 }
10541 try sema.mod.addGlobalAssembly(sema.owner_decl_index, asm_source);
10542 return Air.Inst.Ref.void_value;
10543 }
10544
10545 if (block.is_comptime) {
10546 try sema.requireRuntimeBlock(block, src);
10547 }
10548
10530 if (outputs_len > 1) {10549 if (outputs_len > 1) {
10531 return sema.fail(block, src, "TODO implement Sema for asm with more than 1 output", .{});10550 return sema.fail(block, src, "TODO implement Sema for asm with more than 1 output", .{});
10532 }10551 }
...@@ -10591,7 +10610,6 @@ fn zirAsm(...@@ -10591,7 +10610,6 @@ fn zirAsm(
10591 needed_capacity += name.*.len / 4 + 1;10610 needed_capacity += name.*.len / 4 + 1;
10592 }10611 }
1059310612
10594 const asm_source = sema.code.nullTerminatedString(extra.data.asm_source);
10595 needed_capacity += (asm_source.len + 3) / 4;10613 needed_capacity += (asm_source.len + 3) / 4;
1059610614
10597 const gpa = sema.gpa;10615 const gpa = sema.gpa;
src/codegen/llvm.zig+14
...@@ -476,6 +476,19 @@ pub const Object = struct {...@@ -476,6 +476,19 @@ pub const Object = struct {
476 _ = builder.buildRet(is_lt);476 _ = builder.buildRet(is_lt);
477 }477 }
478478
479 fn genModuleLevelAssembly(object: *Object, comp: *Compilation) !void {
480 const mod = comp.bin_file.options.module.?;
481 if (mod.global_assembly.count() == 0) return;
482 var buffer = std.ArrayList(u8).init(comp.gpa);
483 defer buffer.deinit();
484 var it = mod.global_assembly.iterator();
485 while (it.next()) |kv| {
486 try buffer.appendSlice(kv.value_ptr.*);
487 try buffer.append('\n');
488 }
489 object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1);
490 }
491
479 pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void {492 pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void {
480 var sub_prog_node = prog_node.start("LLVM Emit Object", 0);493 var sub_prog_node = prog_node.start("LLVM Emit Object", 0);
481 sub_prog_node.activate();494 sub_prog_node.activate();
...@@ -484,6 +497,7 @@ pub const Object = struct {...@@ -484,6 +497,7 @@ pub const Object = struct {
484497
485 try self.genErrorNameTable(comp);498 try self.genErrorNameTable(comp);
486 try self.genCmpLtErrorsLenFunction(comp);499 try self.genCmpLtErrorsLenFunction(comp);
500 try self.genModuleLevelAssembly(comp);
487501
488 if (self.di_builder) |dib| {502 if (self.di_builder) |dib| {
489 // When lowering debug info for pointers, we emitted the element types as503 // When lowering debug info for pointers, we emitted the element types as
src/codegen/llvm/bindings.zig+3
...@@ -381,6 +381,9 @@ pub const Module = opaque {...@@ -381,6 +381,9 @@ pub const Module = opaque {
381381
382 pub const createDIBuilder = ZigLLVMCreateDIBuilder;382 pub const createDIBuilder = ZigLLVMCreateDIBuilder;
383 extern fn ZigLLVMCreateDIBuilder(module: *const Module, allow_unresolved: bool) *DIBuilder;383 extern fn ZigLLVMCreateDIBuilder(module: *const Module, allow_unresolved: bool) *DIBuilder;
384
385 pub const setModuleInlineAsm2 = LLVMSetModuleInlineAsm2;
386 extern fn LLVMSetModuleInlineAsm2(M: *const Module, Asm: [*]const u8, Len: usize) void;
384};387};
385388
386pub const lookupIntrinsicID = LLVMLookupIntrinsicID;389pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
test/behavior/asm.zig-1
...@@ -23,7 +23,6 @@ test "module level assembly" {...@@ -23,7 +23,6 @@ test "module level assembly" {
23 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO23 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO24 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
25 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO25 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
26 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
2726
28 if (is_x86_64_linux) {27 if (is_x86_64_linux) {
29 try expect(this_is_my_alias() == 1234);28 try expect(this_is_my_alias() == 1234);