| author | |
| committer | |
| log | 1b432b557608bd047afaad71ffb7bd2ddf23c444 |
| tree | 0930ca5f5f1e78460a75dc5d86e243fccb5027ba |
| parent | 0bebb688fbc4c6ffb88a2b0aefcf536143bb5f2e |
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. |
| 152 | decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{}, | 152 | decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{}, |
| 153 | 153 | ||
| 154 | global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{}, | ||
| 155 | |||
| 154 | const MonomorphedFuncsSet = std.HashMapUnmanaged( | 156 | const 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 { |
| 2831 | 2833 | ||
| 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 | } |
| 2835 | 2838 | ||
| 2836 | pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { | 2839 | 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,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 { |
| 5714 | fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void { | 5720 | fn 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 | |||
| 5724 | pub 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; |
| 10519 | 10519 | ||
| 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 land | 10521 | // 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 | } |
| 10529 | 10525 | ||
| 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 | } |
| 10593 | 10612 | ||
| 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; |
| 10596 | 10614 | ||
| 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 | } |
| 478 | 478 | ||
| 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 { |
| 484 | 497 | ||
| 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); | ||
| 487 | 501 | ||
| 488 | if (self.di_builder) |dib| { | 502 | if (self.di_builder) |dib| { |
| 489 | // When lowering debug info for pointers, we emitted the element types as | 503 | // 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 { |
| 381 | 381 | ||
| 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 | }; |
| 385 | 388 | ||
| 386 | pub const lookupIntrinsicID = LLVMLookupIntrinsicID; | 389 | pub 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; // TODO | 23 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 24 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 24 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 25 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 25 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 26 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO | ||
| 27 | 26 | ||
| 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); |