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
signaturelock-open 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 {
253253 return @enumToInt(val);
254254}
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
256270// types
257271pub const element_type: u8 = 0x70;
258272pub const function_type: u8 = 0x60;
src/codegen/wasm.zig+20-8
......@@ -163,13 +163,18 @@ pub const Context = struct {
163163 try self.genFunctype();
164164 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
169166 // Write instructions
170167 // TODO: check for and handle death of instructions
171168 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
173178 try self.genBody(mod_fn.body);
174179
175180 // finally, write our local types at the 'offset' position
......@@ -239,9 +244,16 @@ pub const Context = struct {
239244
240245 fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {
241246 const func_inst = inst.func.castTag(.constant).?;
242 const func = func_inst.val.castTag(.function).?.data;
243 const target = func.owner_decl;
244 const target_ty = target.typed_value.most_recent.typed_value.ty;
247 const func_val = inst.func.value().?;
248
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
246258 for (inst.args) |arg| {
247259 const arg_val = self.resolveInst(arg);
......@@ -495,7 +507,7 @@ pub const Context = struct {
495507 }
496508
497509 fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue {
498 // of operand has codegen bits we should break with a value
510 // if operand has codegen bits we should break with a value
499511 if (br.operand.ty.hasCodeGenBits()) {
500512 const operand = self.resolveInst(br.operand);
501513 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 {
8585 const typed_value = decl.typed_value.most_recent.typed_value;
8686 if (typed_value.ty.zigTypeTag() != .Fn)
8787 return error.TODOImplementNonFnDeclsForWasm;
88 if (typed_value.val.tag() == .extern_fn)
89 return error.TODOImplementExternFnDeclsForWasm;
9088
9189 if (decl.fn_link.wasm) |*fn_data| {
9290 fn_data.functype.items.len = 0;
......@@ -184,17 +182,63 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
184182 );
185183 }
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
187223 // Function section
188224 {
189225 const header_offset = try reserveVecSectionHeader(file);
190226 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 }
192236 try writeVecSectionHeader(
193237 file,
194238 header_offset,
195239 .function,
196240 @intCast(u32, (try file.getPos()) - header_offset - header_size),
197 @intCast(u32, self.funcs.items.len),
241 count,
198242 );
199243 }
200244
......@@ -214,7 +258,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
214258 // Type of the export
215259 try writer.writeByte(0x00);
216260 // Exported function index
217 try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?);
261 try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).? + 1);
218262 },
219263 else => return error.TODOImplementNonFnDeclsForWasm,
220264 }
......@@ -235,7 +279,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
235279 {
236280 const header_offset = try reserveVecSectionHeader(file);
237281 const writer = file.writer();
282 var count: u32 = 0;
238283 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 }
239289 const fn_data = &decl.fn_link.wasm.?;
240290
241291 // Write the already generated code to the file, inserting
......@@ -247,18 +297,20 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
247297 // Use a fixed width here to make calculating the code size
248298 // in codegen.wasm.gen() simpler.
249299 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);
251302 try writer.writeAll(&buf);
252303 }
253304
254305 try writer.writeAll(fn_data.code.items[current..]);
306 count += 1;
255307 }
256308 try writeVecSectionHeader(
257309 file,
258310 header_offset,
259311 .code,
260312 @intCast(u32, (try file.getPos()) - header_offset - header_size),
261 @intCast(u32, self.funcs.items.len),
313 count,
262314 );
263315 }
264316}