mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 08:45:52 +00:00
Basic "Hello world" working
This commit is contained in:
parent
9f744f19e7
commit
00b2e31589
@ -163,25 +163,23 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
|
||||
.global_get => return .global_get,
|
||||
.global_set => return .global_set,
|
||||
|
||||
.load => if (args.width) |width|
|
||||
switch (width) {
|
||||
8 => switch (args.valtype1.?) {
|
||||
.i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u,
|
||||
.i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u,
|
||||
.f32, .f64 => unreachable,
|
||||
},
|
||||
16 => switch (args.valtype1.?) {
|
||||
.i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u,
|
||||
.i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u,
|
||||
.f32, .f64 => unreachable,
|
||||
},
|
||||
32 => switch (args.valtype1.?) {
|
||||
.i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,
|
||||
.i32, .f32, .f64 => unreachable,
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
else switch (args.valtype1.?) {
|
||||
.load => if (args.width) |width| switch (width) {
|
||||
8 => switch (args.valtype1.?) {
|
||||
.i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u,
|
||||
.i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u,
|
||||
.f32, .f64 => unreachable,
|
||||
},
|
||||
16 => switch (args.valtype1.?) {
|
||||
.i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u,
|
||||
.i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u,
|
||||
.f32, .f64 => unreachable,
|
||||
},
|
||||
32 => switch (args.valtype1.?) {
|
||||
.i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,
|
||||
.i32, .f32, .f64 => unreachable,
|
||||
},
|
||||
else => unreachable,
|
||||
} else switch (args.valtype1.?) {
|
||||
.i32 => return .i32_load,
|
||||
.i64 => return .i64_load,
|
||||
.f32 => return .f32_load,
|
||||
@ -469,6 +467,13 @@ test "Wasm - buildOpcode" {
|
||||
testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);
|
||||
}
|
||||
|
||||
pub const Result = union(enum) {
|
||||
/// The codegen bytes have been appended to `Context.code`
|
||||
appended: void,
|
||||
/// The data is managed externally and are part of the `Result`
|
||||
externally_managed: []const u8,
|
||||
};
|
||||
|
||||
/// Hashmap to store generated `WValue` for each `Inst`
|
||||
pub const ValueTable = std.AutoHashMapUnmanaged(*Inst, WValue);
|
||||
|
||||
@ -504,6 +509,8 @@ pub const Context = struct {
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
CodegenFail,
|
||||
/// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed
|
||||
AnalysisFail,
|
||||
};
|
||||
|
||||
pub fn deinit(self: *Context) void {
|
||||
@ -604,48 +611,65 @@ pub const Context = struct {
|
||||
}
|
||||
|
||||
/// Generates the wasm bytecode for the function declaration belonging to `Context`
|
||||
pub fn gen(self: *Context) InnerError!void {
|
||||
pub fn gen(self: *Context) InnerError!Result {
|
||||
assert(self.code.items.len == 0);
|
||||
try self.genFunctype();
|
||||
|
||||
// Write instructions
|
||||
// TODO: check for and handle death of instructions
|
||||
const tv = self.decl.typed_value.most_recent.typed_value;
|
||||
const mod_fn = blk: {
|
||||
if (tv.val.castTag(.function)) |func| break :blk func.data;
|
||||
if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions
|
||||
return self.fail(.{ .node_offset = 0 }, "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()});
|
||||
};
|
||||
switch (tv.ty.zigTypeTag()) {
|
||||
.Fn => {
|
||||
try self.genFunctype();
|
||||
|
||||
// Reserve space to write the size after generating the code as well as space for locals count
|
||||
try self.code.resize(10);
|
||||
// Write instructions
|
||||
// TODO: check for and handle death of instructions
|
||||
const mod_fn = blk: {
|
||||
if (tv.val.castTag(.function)) |func| break :blk func.data;
|
||||
if (tv.val.castTag(.extern_fn)) |ext_fn| return Result.appended; // don't need code body for extern functions
|
||||
return self.fail(.{ .node_offset = 0 }, "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()});
|
||||
};
|
||||
|
||||
try self.genBody(mod_fn.body);
|
||||
// Reserve space to write the size after generating the code as well as space for locals count
|
||||
try self.code.resize(10);
|
||||
|
||||
// finally, write our local types at the 'offset' position
|
||||
{
|
||||
leb.writeUnsignedFixed(5, self.code.items[5..10], @intCast(u32, self.locals.items.len));
|
||||
try self.genBody(mod_fn.body);
|
||||
|
||||
// offset into 'code' section where we will put our locals types
|
||||
var local_offset: usize = 10;
|
||||
// finally, write our local types at the 'offset' position
|
||||
{
|
||||
leb.writeUnsignedFixed(5, self.code.items[5..10], @intCast(u32, self.locals.items.len));
|
||||
|
||||
// emit the actual locals amount
|
||||
for (self.locals.items) |local| {
|
||||
var buf: [6]u8 = undefined;
|
||||
leb.writeUnsignedFixed(5, buf[0..5], @as(u32, 1));
|
||||
buf[5] = local;
|
||||
try self.code.insertSlice(local_offset, &buf);
|
||||
local_offset += 6;
|
||||
}
|
||||
// offset into 'code' section where we will put our locals types
|
||||
var local_offset: usize = 10;
|
||||
|
||||
// emit the actual locals amount
|
||||
for (self.locals.items) |local| {
|
||||
var buf: [6]u8 = undefined;
|
||||
leb.writeUnsignedFixed(5, buf[0..5], @as(u32, 1));
|
||||
buf[5] = local;
|
||||
try self.code.insertSlice(local_offset, &buf);
|
||||
local_offset += 6;
|
||||
}
|
||||
}
|
||||
|
||||
const writer = self.code.writer();
|
||||
try writer.writeByte(wasm.opcode(.end));
|
||||
|
||||
// Fill in the size of the generated code to the reserved space at the
|
||||
// beginning of the buffer.
|
||||
const size = self.code.items.len - 5 + self.decl.fn_link.wasm.?.idx_refs.items.len * 5;
|
||||
leb.writeUnsignedFixed(5, self.code.items[0..5], @intCast(u32, size));
|
||||
|
||||
// codegen data has been appended to `code`
|
||||
return Result.appended;
|
||||
},
|
||||
.Array => {
|
||||
if (tv.val.castTag(.bytes)) |payload| {
|
||||
if (tv.ty.sentinel()) |sentinel| {
|
||||
// TODO, handle sentinel correctly
|
||||
}
|
||||
return Result{ .externally_managed = payload.data };
|
||||
} else return self.fail(.{ .node_offset = 0 }, "TODO implement gen for more kinds of arrays", .{});
|
||||
},
|
||||
else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: Implement zig type codegen for type: '{s}'", .{tag}),
|
||||
}
|
||||
|
||||
const writer = self.code.writer();
|
||||
try writer.writeByte(wasm.opcode(.end));
|
||||
|
||||
// Fill in the size of the generated code to the reserved space at the
|
||||
// beginning of the buffer.
|
||||
const size = self.code.items.len - 5 + self.decl.fn_link.wasm.?.idx_refs.items.len * 5;
|
||||
leb.writeUnsignedFixed(5, self.code.items[0..5], @intCast(u32, size));
|
||||
}
|
||||
|
||||
fn genInst(self: *Context, inst: *Inst) InnerError!WValue {
|
||||
|
||||
@ -29,6 +29,32 @@ pub const FnData = struct {
|
||||
idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{},
|
||||
};
|
||||
|
||||
/// Data section of the wasm binary
|
||||
/// Each declaration will have its own 'data_segment' within the section
|
||||
/// where the offset is calculated using the previous segments and the content length
|
||||
/// of the data
|
||||
pub const DataSection = struct {
|
||||
segments: std.AutoArrayHashMapUnmanaged(*const Module.Decl, []const u8) = .{},
|
||||
|
||||
/// Returns the offset into the data segment based on a given `Decl`
|
||||
pub fn offset(self: DataSection, decl: *const Module.Decl) u32 {
|
||||
var cur_offset: u32 = 0;
|
||||
return for (self.segments.items()) |entry| {
|
||||
if (entry.key == decl) break cur_offset;
|
||||
cur_offset += @intCast(u32, entry.value.len);
|
||||
} else cur_offset;
|
||||
}
|
||||
|
||||
/// Returns the total payload size of the data section
|
||||
pub fn size(self: DataSection) u32 {
|
||||
var total: u32 = 0;
|
||||
for (self.segments.items()) |entry| {
|
||||
total += @intCast(u32, entry.value.len);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
};
|
||||
|
||||
base: link.File,
|
||||
|
||||
/// List of all function Decls to be written to the output file. The index of
|
||||
@ -45,6 +71,10 @@ ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
|
||||
/// to support existing code.
|
||||
/// TODO: Allow setting this through a flag?
|
||||
host_name: []const u8 = "env",
|
||||
/// Map of declarations with its bytes payload, used to keep track of all data segments
|
||||
/// that needs to be emit when creating the wasm binary.
|
||||
/// The `DataSection`'s lifetime must be kept alive until the linking stage.
|
||||
data: DataSection = .{},
|
||||
|
||||
pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
|
||||
assert(options.object_format == .wasm);
|
||||
@ -52,7 +82,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
|
||||
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
|
||||
// TODO: read the file and keep valid parts instead of truncating
|
||||
const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
|
||||
errdefer file.close();
|
||||
|
||||
@ -92,14 +122,13 @@ pub fn deinit(self: *Wasm) void {
|
||||
}
|
||||
self.funcs.deinit(self.base.allocator);
|
||||
self.ext_funcs.deinit(self.base.allocator);
|
||||
self.data.segments.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 {
|
||||
const typed_value = decl.typed_value.most_recent.typed_value;
|
||||
if (typed_value.ty.zigTypeTag() != .Fn)
|
||||
return error.TODOImplementNonFnDeclsForWasm;
|
||||
|
||||
if (decl.fn_link.wasm) |*fn_data| {
|
||||
fn_data.functype.items.len = 0;
|
||||
@ -111,6 +140,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
|
||||
switch (decl.typed_value.most_recent.typed_value.val.tag()) {
|
||||
.function => try self.funcs.append(self.base.allocator, decl),
|
||||
.extern_fn => try self.ext_funcs.append(self.base.allocator, decl),
|
||||
.bytes => {},
|
||||
else => return error.TODOImplementNonFnDeclsForWasm,
|
||||
}
|
||||
}
|
||||
@ -132,7 +162,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
|
||||
defer context.deinit();
|
||||
|
||||
// generate the 'code' section for the function declaration
|
||||
context.gen() catch |err| switch (err) {
|
||||
const result = context.gen() catch |err| switch (err) {
|
||||
error.CodegenFail => {
|
||||
decl.analysis = .codegen_failure;
|
||||
try module.failed_decls.put(module.gpa, decl, context.err_msg);
|
||||
@ -141,15 +171,24 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
|
||||
else => |e| return err,
|
||||
};
|
||||
|
||||
// as locals are patched afterwards, the offsets of funcidx's are off,
|
||||
// here we update them to correct them
|
||||
for (decl.fn_link.wasm.?.idx_refs.items) |*func| {
|
||||
// For each local, add 6 bytes (count + type)
|
||||
func.offset += @intCast(u32, context.locals.items.len * 6);
|
||||
}
|
||||
switch (typed_value.ty.zigTypeTag()) {
|
||||
.Fn => {
|
||||
// as locals are patched afterwards, the offsets of funcidx's are off,
|
||||
// here we update them to correct them
|
||||
for (decl.fn_link.wasm.?.idx_refs.items) |*func| {
|
||||
// For each local, add 6 bytes (count + type)
|
||||
func.offset += @intCast(u32, context.locals.items.len * 6);
|
||||
}
|
||||
|
||||
fn_data.functype = context.func_type_data.toUnmanaged();
|
||||
fn_data.code = context.code.toUnmanaged();
|
||||
fn_data.functype = context.func_type_data.toUnmanaged();
|
||||
fn_data.code = context.code.toUnmanaged();
|
||||
},
|
||||
.Array => switch (result) {
|
||||
.appended => unreachable,
|
||||
.externally_managed => |payload| try self.data.segments.put(self.base.allocator, decl, payload),
|
||||
},
|
||||
else => return error.TODO,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateDeclExports(
|
||||
@ -257,6 +296,22 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
|
||||
);
|
||||
}
|
||||
|
||||
// Memory section
|
||||
if (self.data.size() != 0) {
|
||||
const header_offset = try reserveVecSectionHeader(file);
|
||||
const writer = file.writer();
|
||||
|
||||
try leb.writeULEB128(writer, @as(u32, 0));
|
||||
try leb.writeULEB128(writer, @as(u32, 1));
|
||||
try writeVecSectionHeader(
|
||||
file,
|
||||
header_offset,
|
||||
.memory,
|
||||
@intCast(u32, (try file.getPos()) - header_offset - header_size),
|
||||
@as(u32, 1),
|
||||
);
|
||||
}
|
||||
|
||||
// Export section
|
||||
if (self.base.options.module) |module| {
|
||||
const header_offset = try reserveVecSectionHeader(file);
|
||||
@ -281,6 +336,16 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// export memory if size is not 0
|
||||
if (self.data.size() != 0) {
|
||||
try leb.writeULEB128(writer, @intCast(u32, "memory".len));
|
||||
try writer.writeAll("memory");
|
||||
try writer.writeByte(wasm.externalKind(.memory));
|
||||
try leb.writeULEB128(writer, @as(u32, 0)); // only 1 memory 'object' can exist
|
||||
count += 1;
|
||||
}
|
||||
|
||||
try writeVecSectionHeader(
|
||||
file,
|
||||
header_offset,
|
||||
@ -320,6 +385,38 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
|
||||
@intCast(u32, self.funcs.items.len),
|
||||
);
|
||||
}
|
||||
|
||||
// Data section
|
||||
{
|
||||
const header_offset = try reserveVecSectionHeader(file);
|
||||
const writer = file.writer();
|
||||
var offset: i32 = 0;
|
||||
for (self.data.segments.items()) |entry| {
|
||||
// index to memory section (always 0 in current wasm version)
|
||||
try leb.writeULEB128(writer, @as(u32, 0));
|
||||
|
||||
// offset into data section
|
||||
try writer.writeByte(wasm.opcode(.i32_const));
|
||||
try leb.writeILEB128(writer, offset);
|
||||
try writer.writeByte(wasm.opcode(.end));
|
||||
|
||||
// payload size
|
||||
const len = @intCast(u32, entry.value.len);
|
||||
try leb.writeULEB128(writer, len);
|
||||
|
||||
// write payload
|
||||
try writer.writeAll(entry.value);
|
||||
offset += @bitCast(i32, len);
|
||||
}
|
||||
|
||||
try writeVecSectionHeader(
|
||||
file,
|
||||
header_offset,
|
||||
.data,
|
||||
@intCast(u32, (try file.getPos()) - header_offset - header_size),
|
||||
@intCast(u32, self.data.segments.items().len),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user