mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-07 14:24:43 +00:00
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.
This commit is contained in:
parent
0bebb688fb
commit
1b432b5576
5 changed files with 55 additions and 6 deletions
|
|
@ -151,6 +151,8 @@ allocated_decls: std.SegmentedList(Decl, 0) = .{},
|
||||||
/// When a Decl object is freed from `allocated_decls`, it is pushed into this stack.
|
/// When a Decl object is freed from `allocated_decls`, it is pushed into this stack.
|
||||||
decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{},
|
decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{},
|
||||||
|
|
||||||
|
global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{},
|
||||||
|
|
||||||
const MonomorphedFuncsSet = std.HashMapUnmanaged(
|
const MonomorphedFuncsSet = std.HashMapUnmanaged(
|
||||||
*Fn,
|
*Fn,
|
||||||
void,
|
void,
|
||||||
|
|
@ -2831,6 +2833,7 @@ pub fn deinit(mod: *Module) void {
|
||||||
|
|
||||||
mod.decls_free_list.deinit(gpa);
|
mod.decls_free_list.deinit(gpa);
|
||||||
mod.allocated_decls.deinit(gpa);
|
mod.allocated_decls.deinit(gpa);
|
||||||
|
mod.global_assembly.deinit(gpa);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
|
pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
|
||||||
|
|
@ -2842,6 +2845,9 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
|
||||||
if (decl.deletion_flag) {
|
if (decl.deletion_flag) {
|
||||||
assert(mod.deletion_set.swapRemove(decl_index));
|
assert(mod.deletion_set.swapRemove(decl_index));
|
||||||
}
|
}
|
||||||
|
if (mod.global_assembly.fetchRemove(decl_index)) |kv| {
|
||||||
|
gpa.free(kv.value);
|
||||||
|
}
|
||||||
if (decl.has_tv) {
|
if (decl.has_tv) {
|
||||||
if (decl.getInnerNamespace()) |namespace| {
|
if (decl.getInnerNamespace()) |namespace| {
|
||||||
namespace.destroyDecls(mod);
|
namespace.destroyDecls(mod);
|
||||||
|
|
@ -5714,3 +5720,12 @@ pub fn markDeclAlive(mod: *Module, decl: *Decl) void {
|
||||||
fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void {
|
fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void {
|
||||||
return mod.markDeclAlive(mod.declPtr(decl_index));
|
return mod.markDeclAlive(mod.declPtr(decl_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void {
|
||||||
|
try mod.global_assembly.ensureUnusedCapacity(mod.gpa, 1);
|
||||||
|
|
||||||
|
const duped_source = try mod.gpa.dupe(u8, source);
|
||||||
|
errdefer mod.gpa.free(duped_source);
|
||||||
|
|
||||||
|
mod.global_assembly.putAssumeCapacityNoClobber(decl_index, duped_source);
|
||||||
|
}
|
||||||
|
|
|
||||||
28
src/Sema.zig
28
src/Sema.zig
|
|
@ -10517,16 +10517,35 @@ fn zirAsm(
|
||||||
const is_volatile = @truncate(u1, extended.small >> 15) != 0;
|
const is_volatile = @truncate(u1, extended.small >> 15) != 0;
|
||||||
const is_global_assembly = sema.func == null;
|
const is_global_assembly = sema.func == null;
|
||||||
|
|
||||||
if (block.is_comptime and !is_global_assembly) {
|
|
||||||
try sema.requireRuntimeBlock(block, src);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extra.data.asm_source == 0) {
|
if (extra.data.asm_source == 0) {
|
||||||
// This can move to become an AstGen error after inline assembly improvements land
|
// This can move to become an AstGen error after inline assembly improvements land
|
||||||
// and stage1 code matches stage2 code.
|
// and stage1 code matches stage2 code.
|
||||||
return sema.fail(block, src, "assembly code must use string literal syntax", .{});
|
return sema.fail(block, src, "assembly code must use string literal syntax", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const asm_source = sema.code.nullTerminatedString(extra.data.asm_source);
|
||||||
|
|
||||||
|
if (is_global_assembly) {
|
||||||
|
if (outputs_len != 0) {
|
||||||
|
return sema.fail(block, src, "module-level assembly does not support outputs", .{});
|
||||||
|
}
|
||||||
|
if (inputs_len != 0) {
|
||||||
|
return sema.fail(block, src, "module-level assembly does not support inputs", .{});
|
||||||
|
}
|
||||||
|
if (clobbers_len != 0) {
|
||||||
|
return sema.fail(block, src, "module-level assembly does not support clobbers", .{});
|
||||||
|
}
|
||||||
|
if (is_volatile) {
|
||||||
|
return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{});
|
||||||
|
}
|
||||||
|
try sema.mod.addGlobalAssembly(sema.owner_decl_index, asm_source);
|
||||||
|
return Air.Inst.Ref.void_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block.is_comptime) {
|
||||||
|
try sema.requireRuntimeBlock(block, src);
|
||||||
|
}
|
||||||
|
|
||||||
if (outputs_len > 1) {
|
if (outputs_len > 1) {
|
||||||
return sema.fail(block, src, "TODO implement Sema for asm with more than 1 output", .{});
|
return sema.fail(block, src, "TODO implement Sema for asm with more than 1 output", .{});
|
||||||
}
|
}
|
||||||
|
|
@ -10591,7 +10610,6 @@ fn zirAsm(
|
||||||
needed_capacity += name.*.len / 4 + 1;
|
needed_capacity += name.*.len / 4 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const asm_source = sema.code.nullTerminatedString(extra.data.asm_source);
|
|
||||||
needed_capacity += (asm_source.len + 3) / 4;
|
needed_capacity += (asm_source.len + 3) / 4;
|
||||||
|
|
||||||
const gpa = sema.gpa;
|
const gpa = sema.gpa;
|
||||||
|
|
|
||||||
|
|
@ -476,6 +476,19 @@ pub const Object = struct {
|
||||||
_ = builder.buildRet(is_lt);
|
_ = builder.buildRet(is_lt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn genModuleLevelAssembly(object: *Object, comp: *Compilation) !void {
|
||||||
|
const mod = comp.bin_file.options.module.?;
|
||||||
|
if (mod.global_assembly.count() == 0) return;
|
||||||
|
var buffer = std.ArrayList(u8).init(comp.gpa);
|
||||||
|
defer buffer.deinit();
|
||||||
|
var it = mod.global_assembly.iterator();
|
||||||
|
while (it.next()) |kv| {
|
||||||
|
try buffer.appendSlice(kv.value_ptr.*);
|
||||||
|
try buffer.append('\n');
|
||||||
|
}
|
||||||
|
object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void {
|
pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void {
|
||||||
var sub_prog_node = prog_node.start("LLVM Emit Object", 0);
|
var sub_prog_node = prog_node.start("LLVM Emit Object", 0);
|
||||||
sub_prog_node.activate();
|
sub_prog_node.activate();
|
||||||
|
|
@ -484,6 +497,7 @@ pub const Object = struct {
|
||||||
|
|
||||||
try self.genErrorNameTable(comp);
|
try self.genErrorNameTable(comp);
|
||||||
try self.genCmpLtErrorsLenFunction(comp);
|
try self.genCmpLtErrorsLenFunction(comp);
|
||||||
|
try self.genModuleLevelAssembly(comp);
|
||||||
|
|
||||||
if (self.di_builder) |dib| {
|
if (self.di_builder) |dib| {
|
||||||
// When lowering debug info for pointers, we emitted the element types as
|
// When lowering debug info for pointers, we emitted the element types as
|
||||||
|
|
|
||||||
|
|
@ -381,6 +381,9 @@ pub const Module = opaque {
|
||||||
|
|
||||||
pub const createDIBuilder = ZigLLVMCreateDIBuilder;
|
pub const createDIBuilder = ZigLLVMCreateDIBuilder;
|
||||||
extern fn ZigLLVMCreateDIBuilder(module: *const Module, allow_unresolved: bool) *DIBuilder;
|
extern fn ZigLLVMCreateDIBuilder(module: *const Module, allow_unresolved: bool) *DIBuilder;
|
||||||
|
|
||||||
|
pub const setModuleInlineAsm2 = LLVMSetModuleInlineAsm2;
|
||||||
|
extern fn LLVMSetModuleInlineAsm2(M: *const Module, Asm: [*]const u8, Len: usize) void;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
|
pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ test "module level assembly" {
|
||||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
if (is_x86_64_linux) {
|
if (is_x86_64_linux) {
|
||||||
try expect(this_is_my_alias() == 1234);
|
try expect(this_is_my_alias() == 1234);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue