const std = @import("std"); const Allocator = std.mem.Allocator; const ArrayList = std.ArrayList; const assert = std.debug.assert; const leb = std.debug.leb; const mem = std.mem; const Decl = @import("../Module.zig").Decl; const Inst = @import("../ir.zig").Inst; const Type = @import("../type.zig").Type; const Value = @import("../value.zig").Value; fn genValtype(ty: Type) u8 { return switch (ty.tag()) { .u32, .i32 => 0x7F, .u64, .i64 => 0x7E, .f32 => 0x7D, .f64 => 0x7C, else => @panic("TODO: Implement more types for wasm."), }; } pub fn genFunctype(buf: *ArrayList(u8), decl: *Decl) !void { const ty = decl.typed_value.most_recent.typed_value.ty; const writer = buf.writer(); // functype magic try writer.writeByte(0x60); // param types try leb.writeULEB128(writer, @intCast(u32, ty.fnParamLen())); if (ty.fnParamLen() != 0) { const params = try buf.allocator.alloc(Type, ty.fnParamLen()); defer buf.allocator.free(params); ty.fnParamTypes(params); for (params) |param_type| try writer.writeByte(genValtype(param_type)); } // return type const return_type = ty.fnReturnType(); switch (return_type.tag()) { .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)), else => { try leb.writeULEB128(writer, @as(u32, 1)); try writer.writeByte(genValtype(return_type)); }, } } pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void { assert(buf.items.len == 0); const writer = buf.writer(); // Reserve space to write the size after generating the code try buf.resize(5); // Write the size of the locals vec // TODO: implement locals try leb.writeULEB128(writer, @as(u32, 0)); // Write instructions // TODO: check for and handle death of instructions const tv = decl.typed_value.most_recent.typed_value; const mod_fn = tv.val.cast(Value.Payload.Function).?.func; for (mod_fn.analysis.success.instructions) |inst| try genInst(buf, decl, inst); // Write 'end' opcode try writer.writeByte(0x0B); // Fill in the size of the generated code to the reserved space at the // beginning of the buffer. const size = buf.items.len - 5 + decl.fn_link.wasm.?.idx_refs.items.len * 5; leb.writeUnsignedFixed(5, buf.items[0..5], @intCast(u32, size)); } fn genInst(buf: *ArrayList(u8), decl: *Decl, inst: *Inst) !void { return switch (inst.tag) { .call => genCall(buf, decl, inst.castTag(.call).?), .constant => genConstant(buf, decl, inst.castTag(.constant).?), .dbg_stmt => {}, .ret => genRet(buf, decl, inst.castTag(.ret).?), .retvoid => {}, else => error.TODOImplementMoreWasmCodegen, }; } fn genConstant(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.Constant) !void { const writer = buf.writer(); switch (inst.base.ty.tag()) { .u32 => { try writer.writeByte(0x41); // i32.const try leb.writeILEB128(writer, inst.val.toUnsignedInt()); }, .i32 => { try writer.writeByte(0x41); // i32.const try leb.writeILEB128(writer, inst.val.toSignedInt()); }, .u64 => { try writer.writeByte(0x42); // i64.const try leb.writeILEB128(writer, inst.val.toUnsignedInt()); }, .i64 => { try writer.writeByte(0x42); // i64.const try leb.writeILEB128(writer, inst.val.toSignedInt()); }, .f32 => { try writer.writeByte(0x43); // f32.const // TODO: enforce LE byte order try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32))); }, .f64 => { try writer.writeByte(0x44); // f64.const // TODO: enforce LE byte order try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64))); }, .void => {}, else => return error.TODOImplementMoreWasmCodegen, } } fn genRet(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.UnOp) !void { try genInst(buf, decl, inst.operand); } fn genCall(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.Call) !void { const func_inst = inst.func.castTag(.constant).?; const func_val = func_inst.val.cast(Value.Payload.Function).?; const target = func_val.func.owner_decl; const target_ty = target.typed_value.most_recent.typed_value.ty; if (inst.args.len != 0) return error.TODOImplementMoreWasmCodegen; try buf.append(0x10); // call // The function index immediate argument will be filled in using this data // in link.Wasm.flush(). try decl.fn_link.wasm.?.idx_refs.append(buf.allocator, .{ .offset = @intCast(u32, buf.items.len), .decl = target, }); }