| ... | ... | @@ -551,7 +551,7 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 551 | 551 | initial_stack_value: WValue = .none, |
| 552 | 552 | /// Arguments of this function declaration |
| 553 | 553 | /// This will be set after `resolveCallingConventionValues` |
| 554 | | args: []WValue = undefined, |
| 554 | args: []WValue = &.{}, |
| 555 | 555 | /// This will only be `.none` if the function returns void, or returns an immediate. |
| 556 | 556 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated |
| 557 | 557 | /// before this function returns its execution to the caller. |
| ... | ... | @@ -1117,6 +1117,13 @@ fn moveStack(self: *Self, offset: u32, local: u32) !void { |
| 1117 | 1117 | try self.addLabel(.global_set, 0); |
| 1118 | 1118 | } |
| 1119 | 1119 | |
| 1120 | /// From given zig bitsize, returns the wasm bitsize |
| 1121 | fn toWasmIntBits(bits: u16) ?u16 { |
| 1122 | return for ([_]u16{ 32, 64 }) |wasm_bits| { |
| 1123 | if (bits <= wasm_bits) return wasm_bits; |
| 1124 | } else null; |
| 1125 | } |
| 1126 | |
| 1120 | 1127 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1121 | 1128 | const air_tags = self.air.instructions.items(.tag); |
| 1122 | 1129 | return switch (air_tags[inst]) { |
| ... | ... | @@ -1563,6 +1570,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1563 | 1570 | } |
| 1564 | 1571 | |
| 1565 | 1572 | fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1573 | if (val.isUndefDeep()) return self.emitUndefined(ty); |
| 1566 | 1574 | switch (ty.zigTypeTag()) { |
| 1567 | 1575 | .Int => { |
| 1568 | 1576 | const int_info = ty.intInfo(self.target); |
| ... | ... | @@ -1668,6 +1676,22 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1668 | 1676 | } |
| 1669 | 1677 | } |
| 1670 | 1678 | |
| 1679 | fn emitUndefined(self: *Self, ty: Type) InnerError!void { |
| 1680 | switch (ty.zigTypeTag()) { |
| 1681 | .Int => switch (ty.intInfo(self.target).bits) { |
| 1682 | 0...32 => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))), |
| 1683 | 33...64 => try self.addImm64(0xaaaaaaaaaaaaaaaa), |
| 1684 | else => |bits| return self.fail("Wasm TODO: emitUndefined for integer bitsize: {d}", .{bits}), |
| 1685 | }, |
| 1686 | .Float => switch (ty.floatBits(self.target)) { |
| 1687 | 0...32 => try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) } }), |
| 1688 | 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))), |
| 1689 | else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}), |
| 1690 | }, |
| 1691 | else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}), |
| 1692 | } |
| 1693 | } |
| 1694 | |
| 1671 | 1695 | /// Returns a `Value` as a signed 32 bit value. |
| 1672 | 1696 | /// It's illegal to provide a value with a type that cannot be represented |
| 1673 | 1697 | /// as an integer value. |
| ... | ... | @@ -2233,19 +2257,21 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2233 | 2257 | const operand = self.resolveInst(ty_op.operand); |
| 2234 | 2258 | const pointer_width = self.target.cpu.arch.ptrBitWidth() / 8; |
| 2235 | 2259 | |
| 2236 | | // Get pointer to slice |
| 2237 | | try self.emitWValue(operand); |
| 2238 | | // length of slice is stored after the pointer of the slice |
| 2239 | | const extra_index = try self.addExtra(Mir.MemArg{ |
| 2240 | | .offset = pointer_width, |
| 2241 | | .alignment = pointer_width, |
| 2242 | | }); |
| 2243 | | try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } }); |
| 2260 | return try self.load(operand, Type.usize, pointer_width); |
| 2244 | 2261 | |
| 2245 | | const result = try self.allocLocal(Type.initTag(.i32)); // pointer is always i32 |
| 2246 | | // store slice length in local |
| 2247 | | try self.addLabel(.local_set, result.local); |
| 2248 | | return result; |
| 2262 | // // Get pointer to slice |
| 2263 | // try self.emitWValue(operand); |
| 2264 | // // length of slice is stored after the pointer of the slice |
| 2265 | // const extra_index = try self.addExtra(Mir.MemArg{ |
| 2266 | // .offset = pointer_width, |
| 2267 | // .alignment = pointer_width, |
| 2268 | // }); |
| 2269 | // try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } }); |
| 2270 | |
| 2271 | // const result = try self.allocLocal(Type.initTag(.i32)); // pointer is always i32 |
| 2272 | // // store slice length in local |
| 2273 | // try self.addLabel(.local_set, result.local); |
| 2274 | // return result; |
| 2249 | 2275 | } |
| 2250 | 2276 | |
| 2251 | 2277 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -2272,3 +2298,70 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2272 | 2298 | try self.addLabel(.local_set, result.local); |
| 2273 | 2299 | return result; |
| 2274 | 2300 | } |
| 2301 | |
| 2302 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2303 | if (self.liveness.isUnused(inst)) return WValue.none; |
| 2304 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2305 | const operand = self.resolveInst(ty_op.operand); |
| 2306 | return try self.load(operand, Type.usize, 0); |
| 2307 | } |
| 2308 | |
| 2309 | fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2310 | if (self.liveness.isUnused(inst)) return WValue.none; |
| 2311 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2312 | const operand = self.resolveInst(ty_op.operand); |
| 2313 | const op_ty = self.air.typeOf(ty_op.operand); |
| 2314 | const int_info = self.air.getRefType(ty_op.ty).intInfo(self.target); |
| 2315 | const wanted_bits = int_info.bits; |
| 2316 | const result = try self.allocLocal(self.air.getRefType(ty_op.ty)); |
| 2317 | const op_bits = op_ty.intInfo(self.target).bits; |
| 2318 | |
| 2319 | const wasm_bits = toWasmIntBits(wanted_bits) orelse |
| 2320 | return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits}); |
| 2321 | |
| 2322 | // Use wasm's instruction to wrap from 64bit to 32bit integer when possible |
| 2323 | if (op_bits == 64 and wanted_bits == 32) { |
| 2324 | try self.emitWValue(operand); |
| 2325 | try self.addTag(.i32_wrap_i64); |
| 2326 | try self.addLabel(.local_set, result.local); |
| 2327 | return result; |
| 2328 | } |
| 2329 | |
| 2330 | // Any other truncation must be done manually |
| 2331 | if (int_info.signedness == .unsigned) { |
| 2332 | const mask = (@as(u65, 1) << @intCast(u7, wanted_bits)) - 1; |
| 2333 | try self.emitWValue(operand); |
| 2334 | switch (wasm_bits) { |
| 2335 | 32 => { |
| 2336 | try self.addImm32(@bitCast(i32, @intCast(u32, mask))); |
| 2337 | try self.addTag(.i32_and); |
| 2338 | }, |
| 2339 | 64 => { |
| 2340 | try self.addImm64(@intCast(u64, mask)); |
| 2341 | try self.addTag(.i64_and); |
| 2342 | }, |
| 2343 | else => unreachable, |
| 2344 | } |
| 2345 | } else { |
| 2346 | const shift_bits = wasm_bits - wanted_bits; |
| 2347 | try self.emitWValue(operand); |
| 2348 | switch (wasm_bits) { |
| 2349 | 32 => { |
| 2350 | try self.addImm32(@bitCast(i16, shift_bits)); |
| 2351 | try self.addTag(.i32_shl); |
| 2352 | try self.addImm32(@bitCast(i16, shift_bits)); |
| 2353 | try self.addTag(.i32_shr_s); |
| 2354 | }, |
| 2355 | 64 => { |
| 2356 | try self.addImm64(shift_bits); |
| 2357 | try self.addTag(.i64_shl); |
| 2358 | try self.addImm64(shift_bits); |
| 2359 | try self.addTag(.i64_shr_s); |
| 2360 | }, |
| 2361 | else => unreachable, |
| 2362 | } |
| 2363 | } |
| 2364 | |
| 2365 | try self.addLabel(.local_set, result.local); |
| 2366 | return result; |
| 2367 | } |