mirror of
https://github.com/ziglang/zig.git
synced 2025-12-27 16:43:07 +00:00
* update to the new cache hash API
* std.Target defaultVersionRange moves to std.Target.Os.Tag
* std.Target.Os gains getVersionRange which returns a tagged union
* start the process of splitting Module into Compilation and "zig
module".
- The parts of Module having to do with only compiling zig code are
extracted into ZigModule.zig.
- Next step is to rename Module to Compilation.
- After that rename ZigModule back to Module.
* implement proper cache hash usage when compiling C objects, and
properly manage the file lock of the build artifacts.
* make versions optional to match recent changes to master branch.
* proper cache hash integration for compiling zig code
* proper cache hash integration for linking even when not compiling zig
code.
* ELF LLD linking integrates with the caching system. A comment from
the source code:
Here we want to determine whether we can save time by not invoking LLD when the
output is unchanged. None of the linker options or the object files that are being
linked are in the hash that namespaces the directory we are outputting to. Therefore,
we must hash those now, and the resulting digest will form the "id" of the linking
job we are about to perform.
After a successful link, we store the id in the metadata of a symlink named "id.txt" in
the artifact directory. So, now, we check if this symlink exists, and if it matches
our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
* implement disable_c_depfile option
* add tracy to a few more functions
256 lines
8.8 KiB
Zig
256 lines
8.8 KiB
Zig
const Wasm = @This();
|
|
|
|
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const assert = std.debug.assert;
|
|
const fs = std.fs;
|
|
const leb = std.debug.leb;
|
|
|
|
const Module = @import("../ZigModule.zig");
|
|
const Compilation = @import("../Module.zig");
|
|
const codegen = @import("../codegen/wasm.zig");
|
|
const link = @import("../link.zig");
|
|
|
|
/// Various magic numbers defined by the wasm spec
|
|
const spec = struct {
|
|
const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm
|
|
const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1
|
|
|
|
const custom_id = 0;
|
|
const types_id = 1;
|
|
const imports_id = 2;
|
|
const funcs_id = 3;
|
|
const tables_id = 4;
|
|
const memories_id = 5;
|
|
const globals_id = 6;
|
|
const exports_id = 7;
|
|
const start_id = 8;
|
|
const elements_id = 9;
|
|
const code_id = 10;
|
|
const data_id = 11;
|
|
};
|
|
|
|
pub const base_tag = link.File.Tag.wasm;
|
|
|
|
pub const FnData = struct {
|
|
/// Generated code for the type of the function
|
|
functype: std.ArrayListUnmanaged(u8) = .{},
|
|
/// Generated code for the body of the function
|
|
code: std.ArrayListUnmanaged(u8) = .{},
|
|
/// Locations in the generated code where function indexes must be filled in.
|
|
/// This must be kept ordered by offset.
|
|
idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{},
|
|
};
|
|
|
|
base: link.File,
|
|
|
|
/// List of all function Decls to be written to the output file. The index of
|
|
/// each Decl in this list at the time of writing the binary is used as the
|
|
/// function index.
|
|
/// TODO: can/should we access some data structure in Module directly?
|
|
funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
|
|
|
|
pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File {
|
|
assert(options.object_format == .wasm);
|
|
|
|
if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO
|
|
if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO
|
|
|
|
// TODO: read the file and keep vaild parts instead of truncating
|
|
const file = try options.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
|
|
errdefer file.close();
|
|
|
|
const wasm = try allocator.create(Wasm);
|
|
errdefer allocator.destroy(wasm);
|
|
|
|
try file.writeAll(&(spec.magic ++ spec.version));
|
|
|
|
wasm.* = .{
|
|
.base = .{
|
|
.tag = .wasm,
|
|
.options = options,
|
|
.file = file,
|
|
.allocator = allocator,
|
|
},
|
|
};
|
|
|
|
return &wasm.base;
|
|
}
|
|
|
|
pub fn deinit(self: *Wasm) void {
|
|
for (self.funcs.items) |decl| {
|
|
decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
|
|
decl.fn_link.wasm.?.code.deinit(self.base.allocator);
|
|
decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
|
|
}
|
|
self.funcs.deinit(self.base.allocator);
|
|
}
|
|
|
|
// Generate code for the Decl, storing it in memory to be later written to
|
|
// the file on flush().
|
|
pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
|
|
if (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() != .Fn)
|
|
return error.TODOImplementNonFnDeclsForWasm;
|
|
|
|
if (decl.fn_link.wasm) |*fn_data| {
|
|
fn_data.functype.items.len = 0;
|
|
fn_data.code.items.len = 0;
|
|
fn_data.idx_refs.items.len = 0;
|
|
} else {
|
|
decl.fn_link.wasm = .{};
|
|
try self.funcs.append(self.base.allocator, decl);
|
|
}
|
|
const fn_data = &decl.fn_link.wasm.?;
|
|
|
|
var managed_functype = fn_data.functype.toManaged(self.base.allocator);
|
|
var managed_code = fn_data.code.toManaged(self.base.allocator);
|
|
try codegen.genFunctype(&managed_functype, decl);
|
|
try codegen.genCode(&managed_code, decl);
|
|
fn_data.functype = managed_functype.toUnmanaged();
|
|
fn_data.code = managed_code.toUnmanaged();
|
|
}
|
|
|
|
pub fn updateDeclExports(
|
|
self: *Wasm,
|
|
module: *Module,
|
|
decl: *const Module.Decl,
|
|
exports: []const *Module.Export,
|
|
) !void {}
|
|
|
|
pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
|
|
// TODO: remove this assert when non-function Decls are implemented
|
|
assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
|
|
_ = self.funcs.swapRemove(self.getFuncidx(decl).?);
|
|
decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
|
|
decl.fn_link.wasm.?.code.deinit(self.base.allocator);
|
|
decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
|
|
decl.fn_link.wasm = null;
|
|
}
|
|
|
|
pub fn flush(self: *Wasm, comp: *Compilation) !void {
|
|
const file = self.base.file.?;
|
|
const header_size = 5 + 1;
|
|
|
|
// No need to rewrite the magic/version header
|
|
try file.setEndPos(@sizeOf(@TypeOf(spec.magic ++ spec.version)));
|
|
try file.seekTo(@sizeOf(@TypeOf(spec.magic ++ spec.version)));
|
|
|
|
// Type section
|
|
{
|
|
const header_offset = try reserveVecSectionHeader(file);
|
|
for (self.funcs.items) |decl| {
|
|
try file.writeAll(decl.fn_link.wasm.?.functype.items);
|
|
}
|
|
try writeVecSectionHeader(
|
|
file,
|
|
header_offset,
|
|
spec.types_id,
|
|
@intCast(u32, (try file.getPos()) - header_offset - header_size),
|
|
@intCast(u32, self.funcs.items.len),
|
|
);
|
|
}
|
|
|
|
// Function section
|
|
{
|
|
const header_offset = try reserveVecSectionHeader(file);
|
|
const writer = file.writer();
|
|
for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx));
|
|
try writeVecSectionHeader(
|
|
file,
|
|
header_offset,
|
|
spec.funcs_id,
|
|
@intCast(u32, (try file.getPos()) - header_offset - header_size),
|
|
@intCast(u32, self.funcs.items.len),
|
|
);
|
|
}
|
|
|
|
// Export section
|
|
if (self.base.options.zig_module) |module| {
|
|
const header_offset = try reserveVecSectionHeader(file);
|
|
const writer = file.writer();
|
|
var count: u32 = 0;
|
|
for (module.decl_exports.entries.items) |entry| {
|
|
for (entry.value) |exprt| {
|
|
// Export name length + name
|
|
try leb.writeULEB128(writer, @intCast(u32, exprt.options.name.len));
|
|
try writer.writeAll(exprt.options.name);
|
|
|
|
switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
|
|
.Fn => {
|
|
// Type of the export
|
|
try writer.writeByte(0x00);
|
|
// Exported function index
|
|
try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?);
|
|
},
|
|
else => return error.TODOImplementNonFnDeclsForWasm,
|
|
}
|
|
|
|
count += 1;
|
|
}
|
|
}
|
|
try writeVecSectionHeader(
|
|
file,
|
|
header_offset,
|
|
spec.exports_id,
|
|
@intCast(u32, (try file.getPos()) - header_offset - header_size),
|
|
count,
|
|
);
|
|
}
|
|
|
|
// Code section
|
|
{
|
|
const header_offset = try reserveVecSectionHeader(file);
|
|
const writer = file.writer();
|
|
for (self.funcs.items) |decl| {
|
|
const fn_data = &decl.fn_link.wasm.?;
|
|
|
|
// Write the already generated code to the file, inserting
|
|
// function indexes where required.
|
|
var current: u32 = 0;
|
|
for (fn_data.idx_refs.items) |idx_ref| {
|
|
try writer.writeAll(fn_data.code.items[current..idx_ref.offset]);
|
|
current = idx_ref.offset;
|
|
// Use a fixed width here to make calculating the code size
|
|
// in codegen.wasm.genCode() simpler.
|
|
var buf: [5]u8 = undefined;
|
|
leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);
|
|
try writer.writeAll(&buf);
|
|
}
|
|
|
|
try writer.writeAll(fn_data.code.items[current..]);
|
|
}
|
|
try writeVecSectionHeader(
|
|
file,
|
|
header_offset,
|
|
spec.code_id,
|
|
@intCast(u32, (try file.getPos()) - header_offset - header_size),
|
|
@intCast(u32, self.funcs.items.len),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Get the current index of a given Decl in the function list
|
|
/// TODO: we could maintain a hash map to potentially make this
|
|
fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 {
|
|
return for (self.funcs.items) |func, idx| {
|
|
if (func == decl) break @intCast(u32, idx);
|
|
} else null;
|
|
}
|
|
|
|
fn reserveVecSectionHeader(file: fs.File) !u64 {
|
|
// section id + fixed leb contents size + fixed leb vector length
|
|
const header_size = 1 + 5 + 5;
|
|
// TODO: this should be a single lseek(2) call, but fs.File does not
|
|
// currently provide a way to do this.
|
|
try file.seekBy(header_size);
|
|
return (try file.getPos()) - header_size;
|
|
}
|
|
|
|
fn writeVecSectionHeader(file: fs.File, offset: u64, section: u8, size: u32, items: u32) !void {
|
|
var buf: [1 + 5 + 5]u8 = undefined;
|
|
buf[0] = section;
|
|
leb.writeUnsignedFixed(5, buf[1..6], size);
|
|
leb.writeUnsignedFixed(5, buf[6..], items);
|
|
try file.pwriteAll(&buf, offset);
|
|
}
|