authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-02-03 22:15:18+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-02-05 18:06:20+01:00
logaa3e0ff454d06407b4ee347c1cd0c1e09444c52c
tree6d89592d1c81eb2f18620b048fb3a157a1b71dc9
parent300ebbd56090d7c96478293354e24c0f3341ec85
signature Commit is signed but in an unrecognized format.

Create type declarations for extern functions and write the 'import' section


3 files changed, 93 insertions(+), 15 deletions(-)

lib/std/wasm.zig+14
...@@ -253,6 +253,20 @@ pub fn section(val: Section) u8 {...@@ -253,6 +253,20 @@ pub fn section(val: Section) u8 {
253 return @enumToInt(val);253 return @enumToInt(val);
254}254}
255255
256/// The kind of the type when importing or exporting to/from the host environment
257/// https://webassembly.github.io/spec/core/syntax/modules.html
258pub const ExternalKind = enum(u8) {
259 function,
260 table,
261 memory,
262 global,
263};
264
265/// Returns the integer value of a given `ExternalKind`
266pub fn kind(val: ExternalKind) u8 {
267 return @enumToInt(val);
268}
269
256// types270// types
257pub const element_type: u8 = 0x70;271pub const element_type: u8 = 0x70;
258pub const function_type: u8 = 0x60;272pub const function_type: u8 = 0x60;
src/codegen/wasm.zig+20-8
...@@ -163,13 +163,18 @@ pub const Context = struct {...@@ -163,13 +163,18 @@ pub const Context = struct {
163 try self.genFunctype();163 try self.genFunctype();
164 const writer = self.code.writer();164 const writer = self.code.writer();
165165
166 // Reserve space to write the size after generating the code as well as space for locals count
167 try self.code.resize(10);
168
169 // Write instructions166 // Write instructions
170 // TODO: check for and handle death of instructions167 // TODO: check for and handle death of instructions
171 const tv = self.decl.typed_value.most_recent.typed_value;168 const tv = self.decl.typed_value.most_recent.typed_value;
172 const mod_fn = tv.val.castTag(.function).?.data;169 const mod_fn = blk: {
170 if (tv.val.castTag(.function)) |func| break :blk func.data;
171 if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions
172 return self.fail(self.decl.src(), "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()});
173 };
174
175 // Reserve space to write the size after generating the code as well as space for locals count
176 try self.code.resize(10);
177
173 try self.genBody(mod_fn.body);178 try self.genBody(mod_fn.body);
174179
175 // finally, write our local types at the 'offset' position180 // finally, write our local types at the 'offset' position
...@@ -239,9 +244,16 @@ pub const Context = struct {...@@ -239,9 +244,16 @@ pub const Context = struct {
239244
240 fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {245 fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {
241 const func_inst = inst.func.castTag(.constant).?;246 const func_inst = inst.func.castTag(.constant).?;
242 const func = func_inst.val.castTag(.function).?.data;247 const func_val = inst.func.value().?;
243 const target = func.owner_decl;248
244 const target_ty = target.typed_value.most_recent.typed_value.ty;249 const target = blk: {
250 if (func_val.castTag(.function)) |func| {
251 break :blk func.data.owner_decl;
252 } else if (func_val.castTag(.extern_fn)) |ext_fn| {
253 break :blk ext_fn.data;
254 }
255 return self.fail(inst.base.src, "Expected a function, but instead found type '{s}'", .{func_val.tag()});
256 };
245257
246 for (inst.args) |arg| {258 for (inst.args) |arg| {
247 const arg_val = self.resolveInst(arg);259 const arg_val = self.resolveInst(arg);
...@@ -495,7 +507,7 @@ pub const Context = struct {...@@ -495,7 +507,7 @@ pub const Context = struct {
495 }507 }
496508
497 fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue {509 fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue {
498 // of operand has codegen bits we should break with a value510 // if operand has codegen bits we should break with a value
499 if (br.operand.ty.hasCodeGenBits()) {511 if (br.operand.ty.hasCodeGenBits()) {
500 const operand = self.resolveInst(br.operand);512 const operand = self.resolveInst(br.operand);
501 try self.emitWValue(operand);513 try self.emitWValue(operand);
src/link/Wasm.zig+59-7
...@@ -85,8 +85,6 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -85,8 +85,6 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
85 const typed_value = decl.typed_value.most_recent.typed_value;85 const typed_value = decl.typed_value.most_recent.typed_value;
86 if (typed_value.ty.zigTypeTag() != .Fn)86 if (typed_value.ty.zigTypeTag() != .Fn)
87 return error.TODOImplementNonFnDeclsForWasm;87 return error.TODOImplementNonFnDeclsForWasm;
88 if (typed_value.val.tag() == .extern_fn)
89 return error.TODOImplementExternFnDeclsForWasm;
9088
91 if (decl.fn_link.wasm) |*fn_data| {89 if (decl.fn_link.wasm) |*fn_data| {
92 fn_data.functype.items.len = 0;90 fn_data.functype.items.len = 0;
...@@ -184,17 +182,63 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -184,17 +182,63 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
184 );182 );
185 }183 }
186184
185 // Import section
186 {
187 // TODO: implement non-functions imports
188 const header_offset = try reserveVecSectionHeader(file);
189 const writer = file.writer();
190 var count: u32 = 0;
191 for (self.funcs.items) |decl, typeidx| {
192 if (decl.typed_value.most_recent.typed_value.val.tag() != .extern_fn) {
193 continue;
194 }
195
196 // TODO: can we set/save the module name somewhere?
197 // For now, emit "env" like LLVM does
198 const module_name = "env";
199 try leb.writeULEB128(writer, @intCast(u32, module_name.len));
200 try writer.writeAll(module_name);
201
202 // wasm requires the length of the import name and doesn't require a null-termination
203 const decl_len = mem.len(decl.name);
204 try leb.writeULEB128(writer, @intCast(u32, decl_len));
205 try writer.writeAll(decl.name[0..decl_len]);
206
207 // emit kind and the function type
208 try writer.writeByte(wasm.kind(.function));
209 try leb.writeULEB128(writer, @intCast(u32, typeidx));
210
211 count += 1;
212 }
213
214 try writeVecSectionHeader(
215 file,
216 header_offset,
217 .import,
218 @intCast(u32, (try file.getPos()) - header_offset - header_size),
219 count,
220 );
221 }
222
187 // Function section223 // Function section
188 {224 {
189 const header_offset = try reserveVecSectionHeader(file);225 const header_offset = try reserveVecSectionHeader(file);
190 const writer = file.writer();226 const writer = file.writer();
191 for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx));227 var count: u32 = 0;
228 for (self.funcs.items) |decl, typeidx| {
229 // Extern functions only have a type, so skip the function signature section
230 if (decl.typed_value.most_recent.typed_value.val.tag() != .function) {
231 continue;
232 }
233 try leb.writeULEB128(writer, @intCast(u32, typeidx));
234 count += 1;
235 }
192 try writeVecSectionHeader(236 try writeVecSectionHeader(
193 file,237 file,
194 header_offset,238 header_offset,
195 .function,239 .function,
196 @intCast(u32, (try file.getPos()) - header_offset - header_size),240 @intCast(u32, (try file.getPos()) - header_offset - header_size),
197 @intCast(u32, self.funcs.items.len),241 count,
198 );242 );
199 }243 }
200244
...@@ -214,7 +258,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -214,7 +258,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
214 // Type of the export258 // Type of the export
215 try writer.writeByte(0x00);259 try writer.writeByte(0x00);
216 // Exported function index260 // Exported function index
217 try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?);261 try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).? + 1);
218 },262 },
219 else => return error.TODOImplementNonFnDeclsForWasm,263 else => return error.TODOImplementNonFnDeclsForWasm,
220 }264 }
...@@ -235,7 +279,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -235,7 +279,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
235 {279 {
236 const header_offset = try reserveVecSectionHeader(file);280 const header_offset = try reserveVecSectionHeader(file);
237 const writer = file.writer();281 const writer = file.writer();
282 var count: u32 = 0;
238 for (self.funcs.items) |decl| {283 for (self.funcs.items) |decl| {
284 // Do not emit any code for extern functions
285 if (decl.typed_value.most_recent.typed_value.val.tag() != .function) {
286 std.debug.print("Skipping decl: {s}\n", .{decl.name});
287 continue;
288 }
239 const fn_data = &decl.fn_link.wasm.?;289 const fn_data = &decl.fn_link.wasm.?;
240290
241 // Write the already generated code to the file, inserting291 // Write the already generated code to the file, inserting
...@@ -247,18 +297,20 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -247,18 +297,20 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
247 // Use a fixed width here to make calculating the code size297 // Use a fixed width here to make calculating the code size
248 // in codegen.wasm.gen() simpler.298 // in codegen.wasm.gen() simpler.
249 var buf: [5]u8 = undefined;299 var buf: [5]u8 = undefined;
250 leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);300 std.debug.print("idx_ref: {s} - {d}\n", .{ idx_ref.decl.name, self.getFuncidx(idx_ref.decl).? });
301 leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).? - 1);
251 try writer.writeAll(&buf);302 try writer.writeAll(&buf);
252 }303 }
253304
254 try writer.writeAll(fn_data.code.items[current..]);305 try writer.writeAll(fn_data.code.items[current..]);
306 count += 1;
255 }307 }
256 try writeVecSectionHeader(308 try writeVecSectionHeader(
257 file,309 file,
258 header_offset,310 header_offset,
259 .code,311 .code,
260 @intCast(u32, (try file.getPos()) - header_offset - header_size),312 @intCast(u32, (try file.getPos()) - header_offset - header_size),
261 @intCast(u32, self.funcs.items.len),313 count,
262 );314 );
263 }315 }
264}316}