| ... | @@ -623,6 +623,10 @@ fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void { | ... | @@ -623,6 +623,10 @@ fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void { |
| 623 | try self.addInst(.{ .tag = tag, .data = .{ .tag = {} } }); | 623 | try self.addInst(.{ .tag = tag, .data = .{ .tag = {} } }); |
| 624 | } | 624 | } |
| 625 | | 625 | |
| | 626 | fn addExtended(self: *Self, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void { |
| | 627 | try self.addInst(.{ .tag = .extended, .secondary = @enumToInt(opcode), .data = .{ .tag = {} } }); |
| | 628 | } |
| | 629 | |
| 626 | fn addLabel(self: *Self, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void { | 630 | fn addLabel(self: *Self, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void { |
| 627 | try self.addInst(.{ .tag = tag, .data = .{ .label = label } }); | 631 | try self.addInst(.{ .tag = tag, .data = .{ .label = label } }); |
| 628 | } | 632 | } |
| ... | @@ -746,6 +750,13 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type { | ... | @@ -746,6 +750,13 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type { |
| 746 | defer params.deinit(); | 750 | defer params.deinit(); |
| 747 | var returns = std.ArrayList(wasm.Valtype).init(self.gpa); | 751 | var returns = std.ArrayList(wasm.Valtype).init(self.gpa); |
| 748 | defer returns.deinit(); | 752 | defer returns.deinit(); |
| | 753 | const return_type = fn_ty.fnReturnType(); |
| | 754 | |
| | 755 | const want_sret = isByRef(return_type); |
| | 756 | |
| | 757 | if (want_sret) { |
| | 758 | try params.append(try self.typeToValtype(Type.usize)); |
| | 759 | } |
| 749 | | 760 | |
| 750 | // param types | 761 | // param types |
| 751 | if (fn_ty.fnParamLen() != 0) { | 762 | if (fn_ty.fnParamLen() != 0) { |
| ... | @@ -759,11 +770,8 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type { | ... | @@ -759,11 +770,8 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type { |
| 759 | } | 770 | } |
| 760 | | 771 | |
| 761 | // return type | 772 | // return type |
| 762 | const return_type = fn_ty.fnReturnType(); | 773 | if (!want_sret and return_type.hasCodeGenBits()) { |
| 763 | switch (return_type.zigTypeTag()) { | 774 | try returns.append(try self.typeToValtype(return_type)); |
| 764 | .Void, .NoReturn => {}, | | |
| 765 | .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}), | | |
| 766 | else => try returns.append(try self.typeToValtype(return_type)), | | |
| 767 | } | 775 | } |
| 768 | | 776 | |
| 769 | return wasm.Type{ | 777 | return wasm.Type{ |
| ... | @@ -785,6 +793,15 @@ pub fn genFunc(self: *Self) InnerError!Result { | ... | @@ -785,6 +793,15 @@ pub fn genFunc(self: *Self) InnerError!Result { |
| 785 | | 793 | |
| 786 | // Generate MIR for function body | 794 | // Generate MIR for function body |
| 787 | try self.genBody(self.air.getMainBody()); | 795 | try self.genBody(self.air.getMainBody()); |
| | 796 | // In case we have a return value, but the last instruction is a noreturn (such as a while loop) |
| | 797 | // we emit an unreachable instruction to tell the stack validator that part will never be reached. |
| | 798 | if (func_type.returns.len != 0 and self.air.instructions.len > 0) { |
| | 799 | const inst = @intCast(u32, self.air.instructions.len - 1); |
| | 800 | if (self.air.typeOfIndex(inst).isNoReturn()) { |
| | 801 | try self.addTag(.@"unreachable"); |
| | 802 | } |
| | 803 | } |
| | 804 | |
| 788 | // End of function body | 805 | // End of function body |
| 789 | try self.addTag(.end); | 806 | try self.addTag(.end); |
| 790 | | 807 | |
| ... | @@ -1074,6 +1091,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu | ... | @@ -1074,6 +1091,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1074 | .return_value = .none, | 1091 | .return_value = .none, |
| 1075 | }; | 1092 | }; |
| 1076 | errdefer self.gpa.free(result.args); | 1093 | errdefer self.gpa.free(result.args); |
| | 1094 | const ret_ty = fn_ty.fnReturnType(); |
| | 1095 | // Check if we store the result as a pointer to the stack rather than |
| | 1096 | // by value |
| | 1097 | if (isByRef(ret_ty)) { |
| | 1098 | // the sret arg will be passed as first argument, therefore we |
| | 1099 | // set the `return_value` before allocating locals for regular args. |
| | 1100 | result.return_value = .{ .local = self.local_index }; |
| | 1101 | self.local_index += 1; |
| | 1102 | } |
| 1077 | switch (cc) { | 1103 | switch (cc) { |
| 1078 | .Naked => return result, | 1104 | .Naked => return result, |
| 1079 | .Unspecified, .C => { | 1105 | .Unspecified, .C => { |
| ... | @@ -1086,27 +1112,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu | ... | @@ -1086,27 +1112,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1086 | result.args[ty_index] = .{ .local = self.local_index }; | 1112 | result.args[ty_index] = .{ .local = self.local_index }; |
| 1087 | self.local_index += 1; | 1113 | self.local_index += 1; |
| 1088 | } | 1114 | } |
| 1089 | | | |
| 1090 | const ret_ty = fn_ty.fnReturnType(); | | |
| 1091 | if (isByRef(ret_ty)) { | | |
| 1092 | result.return_value = try self.allocLocal(Type.initTag(.i32)); | | |
| 1093 | } | | |
| 1094 | | | |
| 1095 | // Check if we store the result as a pointer to the stack rather than | | |
| 1096 | // by value | | |
| 1097 | if (result.return_value != .none) { | | |
| 1098 | if (self.initial_stack_value == .none) try self.initializeStack(); | | |
| 1099 | const offset = std.math.cast(u32, ret_ty.abiSize(self.target)) catch { | | |
| 1100 | return self.fail("Return type '{}' too big for stack frame", .{ret_ty}); | | |
| 1101 | }; | | |
| 1102 | | | |
| 1103 | try self.moveStack(offset, result.return_value.local); | | |
| 1104 | | | |
| 1105 | // We want to make sure the return value's stack value doesn't get overwritten, | | |
| 1106 | // so set initial stack value to current's position instead. | | |
| 1107 | try self.addLabel(.global_get, 0); | | |
| 1108 | try self.addLabel(.local_set, self.initial_stack_value.local); | | |
| 1109 | } | | |
| 1110 | }, | 1115 | }, |
| 1111 | else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}), | 1116 | else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}), |
| 1112 | } | 1117 | } |
| ... | @@ -1165,10 +1170,16 @@ fn allocStack(self: *Self, ty: Type) !WValue { | ... | @@ -1165,10 +1170,16 @@ fn allocStack(self: *Self, ty: Type) !WValue { |
| 1165 | assert(ty.hasCodeGenBits()); | 1170 | assert(ty.hasCodeGenBits()); |
| 1166 | | 1171 | |
| 1167 | // calculate needed stack space | 1172 | // calculate needed stack space |
| 1168 | const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch { | 1173 | var abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch { |
| 1169 | return self.fail("Given type '{}' too big to fit into stack frame", .{ty}); | 1174 | return self.fail("Given type '{}' too big to fit into stack frame", .{ty}); |
| 1170 | }; | 1175 | }; |
| 1171 | | 1176 | |
| | 1177 | // We store slices as a struct with a pointer field and a length field |
| | 1178 | // both being 'usize' size. |
| | 1179 | if (ty.isSlice()) { |
| | 1180 | abi_size = self.ptrSize() * 2; |
| | 1181 | } |
| | 1182 | |
| 1172 | // allocate a local using wasm's pointer size | 1183 | // allocate a local using wasm's pointer size |
| 1173 | const local = try self.allocLocal(Type.@"usize"); | 1184 | const local = try self.allocLocal(Type.@"usize"); |
| 1174 | try self.moveStack(abi_size, local.local); | 1185 | try self.moveStack(abi_size, local.local); |
| ... | @@ -1256,6 +1267,28 @@ fn isByRef(ty: Type) bool { | ... | @@ -1256,6 +1267,28 @@ fn isByRef(ty: Type) bool { |
| 1256 | } | 1267 | } |
| 1257 | } | 1268 | } |
| 1258 | | 1269 | |
| | 1270 | /// Creates a new local for a pointer that points to memory with given offset. |
| | 1271 | /// This can be used to get a pointer to a struct field, error payload, etc. |
| | 1272 | fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64) InnerError!WValue { |
| | 1273 | // do not perform arithmetic when offset is 0. |
| | 1274 | if (offset == 0) return ptr_value; |
| | 1275 | const result_ptr = try self.allocLocal(Type.usize); |
| | 1276 | try self.emitWValue(ptr_value); |
| | 1277 | switch (self.target.cpu.arch.ptrBitWidth()) { |
| | 1278 | 32 => { |
| | 1279 | try self.addImm32(@bitCast(i32, @intCast(u32, offset))); |
| | 1280 | try self.addTag(.i32_add); |
| | 1281 | }, |
| | 1282 | 64 => { |
| | 1283 | try self.addImm64(offset); |
| | 1284 | try self.addTag(.i64_add); |
| | 1285 | }, |
| | 1286 | else => unreachable, |
| | 1287 | } |
| | 1288 | try self.addLabel(.local_set, result_ptr.local); |
| | 1289 | return result_ptr; |
| | 1290 | } |
| | 1291 | |
| 1259 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | 1292 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1260 | const air_tags = self.air.instructions.items(.tag); | 1293 | const air_tags = self.air.instructions.items(.tag); |
| 1261 | return switch (air_tags[inst]) { | 1294 | return switch (air_tags[inst]) { |
| ... | @@ -1296,16 +1329,17 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1296,16 +1329,17 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1296 | .is_err => self.airIsErr(inst, .i32_ne), | 1329 | .is_err => self.airIsErr(inst, .i32_ne), |
| 1297 | .is_non_err => self.airIsErr(inst, .i32_eq), | 1330 | .is_non_err => self.airIsErr(inst, .i32_eq), |
| 1298 | | 1331 | |
| 1299 | .is_null => self.airIsNull(inst, .i32_ne), | 1332 | .is_null => self.airIsNull(inst, .i32_eq, .value), |
| 1300 | .is_non_null => self.airIsNull(inst, .i32_eq), | 1333 | .is_non_null => self.airIsNull(inst, .i32_ne, .value), |
| 1301 | .is_null_ptr => self.airIsNull(inst, .i32_ne), | 1334 | .is_null_ptr => self.airIsNull(inst, .i32_eq, .ptr), |
| 1302 | .is_non_null_ptr => self.airIsNull(inst, .i32_eq), | 1335 | .is_non_null_ptr => self.airIsNull(inst, .i32_ne, .ptr), |
| 1303 | | 1336 | |
| 1304 | .load => self.airLoad(inst), | 1337 | .load => self.airLoad(inst), |
| 1305 | .loop => self.airLoop(inst), | 1338 | .loop => self.airLoop(inst), |
| | 1339 | .memset => self.airMemset(inst), |
| 1306 | .not => self.airNot(inst), | 1340 | .not => self.airNot(inst), |
| 1307 | .optional_payload => self.airOptionalPayload(inst), | 1341 | .optional_payload => self.airOptionalPayload(inst), |
| 1308 | .optional_payload_ptr => self.airOptionalPayload(inst), | 1342 | .optional_payload_ptr => self.airOptionalPayloadPtr(inst), |
| 1309 | .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst), | 1343 | .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst), |
| 1310 | .ptr_add => self.airPtrBinOp(inst, .add), | 1344 | .ptr_add => self.airPtrBinOp(inst, .add), |
| 1311 | .ptr_sub => self.airPtrBinOp(inst, .sub), | 1345 | .ptr_sub => self.airPtrBinOp(inst, .sub), |
| ... | @@ -1315,17 +1349,21 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1315,17 +1349,21 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1315 | .ret => self.airRet(inst), | 1349 | .ret => self.airRet(inst), |
| 1316 | .ret_ptr => self.airRetPtr(inst), | 1350 | .ret_ptr => self.airRetPtr(inst), |
| 1317 | .ret_load => self.airRetLoad(inst), | 1351 | .ret_load => self.airRetLoad(inst), |
| | 1352 | |
| | 1353 | .slice => self.airSlice(inst), |
| 1318 | .slice_len => self.airSliceLen(inst), | 1354 | .slice_len => self.airSliceLen(inst), |
| 1319 | .slice_elem_val => self.airSliceElemVal(inst), | 1355 | .slice_elem_val => self.airSliceElemVal(inst), |
| 1320 | .slice_elem_ptr => self.airSliceElemPtr(inst), | 1356 | .slice_elem_ptr => self.airSliceElemPtr(inst), |
| 1321 | .slice_ptr => self.airSlicePtr(inst), | 1357 | .slice_ptr => self.airSlicePtr(inst), |
| 1322 | .store => self.airStore(inst), | 1358 | .store => self.airStore(inst), |
| | 1359 | |
| 1323 | .struct_field_ptr => self.airStructFieldPtr(inst), | 1360 | .struct_field_ptr => self.airStructFieldPtr(inst), |
| 1324 | .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0), | 1361 | .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0), |
| 1325 | .struct_field_ptr_index_1 => self.airStructFieldPtrIndex(inst, 1), | 1362 | .struct_field_ptr_index_1 => self.airStructFieldPtrIndex(inst, 1), |
| 1326 | .struct_field_ptr_index_2 => self.airStructFieldPtrIndex(inst, 2), | 1363 | .struct_field_ptr_index_2 => self.airStructFieldPtrIndex(inst, 2), |
| 1327 | .struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3), | 1364 | .struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3), |
| 1328 | .struct_field_val => self.airStructFieldVal(inst), | 1365 | .struct_field_val => self.airStructFieldVal(inst), |
| | 1366 | |
| 1329 | .switch_br => self.airSwitchBr(inst), | 1367 | .switch_br => self.airSwitchBr(inst), |
| 1330 | .trunc => self.airTrunc(inst), | 1368 | .trunc => self.airTrunc(inst), |
| 1331 | .unreach => self.airUnreachable(inst), | 1369 | .unreach => self.airUnreachable(inst), |
| ... | @@ -1353,7 +1391,6 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1353,7 +1391,6 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1353 | // to the stack instead | 1391 | // to the stack instead |
| 1354 | if (self.return_value != .none) { | 1392 | if (self.return_value != .none) { |
| 1355 | try self.store(self.return_value, operand, self.decl.ty.fnReturnType(), 0); | 1393 | try self.store(self.return_value, operand, self.decl.ty.fnReturnType(), 0); |
| 1356 | try self.emitWValue(self.return_value); | | |
| 1357 | } else { | 1394 | } else { |
| 1358 | try self.emitWValue(operand); | 1395 | try self.emitWValue(operand); |
| 1359 | } | 1396 | } |
| ... | @@ -1372,6 +1409,9 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1372,6 +1409,9 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1372 | | 1409 | |
| 1373 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; | 1410 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; |
| 1374 | | 1411 | |
| | 1412 | if (isByRef(child_type)) { |
| | 1413 | return self.return_value; |
| | 1414 | } |
| 1375 | return self.allocStack(child_type); | 1415 | return self.allocStack(child_type); |
| 1376 | } | 1416 | } |
| 1377 | | 1417 | |
| ... | @@ -1381,9 +1421,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1381,9 +1421,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1381 | const ret_ty = self.air.typeOf(un_op).childType(); | 1421 | const ret_ty = self.air.typeOf(un_op).childType(); |
| 1382 | if (!ret_ty.hasCodeGenBits()) return WValue.none; | 1422 | if (!ret_ty.hasCodeGenBits()) return WValue.none; |
| 1383 | | 1423 | |
| 1384 | if (isByRef(ret_ty)) { | 1424 | if (!isByRef(ret_ty)) { |
| 1385 | try self.emitWValue(operand); | | |
| 1386 | } else { | | |
| 1387 | const result = try self.load(operand, ret_ty, 0); | 1425 | const result = try self.load(operand, ret_ty, 0); |
| 1388 | try self.emitWValue(result); | 1426 | try self.emitWValue(result); |
| 1389 | } | 1427 | } |
| ... | @@ -1404,6 +1442,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1404,6 +1442,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1404 | .Pointer => ty.childType(), | 1442 | .Pointer => ty.childType(), |
| 1405 | else => unreachable, | 1443 | else => unreachable, |
| 1406 | }; | 1444 | }; |
| | 1445 | const ret_ty = fn_ty.fnReturnType(); |
| | 1446 | const first_param_sret = isByRef(ret_ty); |
| 1407 | | 1447 | |
| 1408 | const target: ?*Decl = blk: { | 1448 | const target: ?*Decl = blk: { |
| 1409 | const func_val = self.air.value(pl_op.operand) orelse break :blk null; | 1449 | const func_val = self.air.value(pl_op.operand) orelse break :blk null; |
| ... | @@ -1416,6 +1456,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1416,6 +1456,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1416 | return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()}); | 1456 | return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()}); |
| 1417 | }; | 1457 | }; |
| 1418 | | 1458 | |
| | 1459 | const sret = if (first_param_sret) blk: { |
| | 1460 | const sret_local = try self.allocStack(ret_ty); |
| | 1461 | try self.emitWValue(sret_local); |
| | 1462 | break :blk sret_local; |
| | 1463 | } else WValue{ .none = {} }; |
| | 1464 | |
| 1419 | for (args) |arg| { | 1465 | for (args) |arg| { |
| 1420 | const arg_ref = @intToEnum(Air.Inst.Ref, arg); | 1466 | const arg_ref = @intToEnum(Air.Inst.Ref, arg); |
| 1421 | const arg_val = self.resolveInst(arg_ref); | 1467 | const arg_val = self.resolveInst(arg_ref); |
| ... | @@ -1454,42 +1500,35 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1454,42 +1500,35 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1454 | try self.addLabel(.call_indirect, fn_type_index); | 1500 | try self.addLabel(.call_indirect, fn_type_index); |
| 1455 | } | 1501 | } |
| 1456 | | 1502 | |
| 1457 | const ret_ty = fn_ty.fnReturnType(); | 1503 | if (self.liveness.isUnused(inst) or !ret_ty.hasCodeGenBits()) { |
| 1458 | if (!ret_ty.hasCodeGenBits()) return WValue.none; | 1504 | return WValue.none; |
| 1459 | | 1505 | } else if (ret_ty.isNoReturn()) { |
| 1460 | // TODO: Implement this for all aggregate types | 1506 | try self.addTag(.@"unreachable"); |
| 1461 | if (ret_ty.isSlice()) { | 1507 | return WValue.none; |
| 1462 | // first load the values onto the regular stack, before we move the stack pointer | 1508 | } else if (first_param_sret) { |
| 1463 | // to prevent overwriting the return value. | 1509 | return sret; |
| 1464 | const tmp = try self.allocLocal(ret_ty); | 1510 | } else { |
| 1465 | try self.addLabel(.local_set, tmp.local); | 1511 | const result_local = try self.allocLocal(ret_ty); |
| 1466 | const field_ty = Type.@"usize"; | 1512 | try self.addLabel(.local_set, result_local.local); |
| 1467 | const offset = @intCast(u32, field_ty.abiSize(self.target)); | 1513 | return result_local; |
| 1468 | const ptr_local = try self.load(tmp, field_ty, 0); | | |
| 1469 | const len_local = try self.load(tmp, field_ty, offset); | | |
| 1470 | | | |
| 1471 | // As our values are now safe, we reserve space on the virtual stack and | | |
| 1472 | // store the values there. | | |
| 1473 | const result = try self.allocStack(ret_ty); | | |
| 1474 | try self.store(result, ptr_local, field_ty, 0); | | |
| 1475 | try self.store(result, len_local, field_ty, offset); | | |
| 1476 | return result; | | |
| 1477 | } | 1514 | } |
| 1478 | | | |
| 1479 | const result_local = try self.allocLocal(ret_ty); | | |
| 1480 | try self.addLabel(.local_set, result_local.local); | | |
| 1481 | return result_local; | | |
| 1482 | } | 1515 | } |
| 1483 | | 1516 | |
| 1484 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1517 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1485 | const child_type = self.air.typeOfIndex(inst).childType(); | 1518 | const pointee_type = self.air.typeOfIndex(inst).childType(); |
| 1486 | | 1519 | |
| 1487 | // Initialize the stack | 1520 | // Initialize the stack |
| 1488 | if (self.initial_stack_value == .none) { | 1521 | if (self.initial_stack_value == .none) { |
| 1489 | try self.initializeStack(); | 1522 | try self.initializeStack(); |
| 1490 | } | 1523 | } |
| 1491 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; | 1524 | |
| 1492 | return self.allocStack(child_type); | 1525 | if (!pointee_type.hasCodeGenBits()) { |
| | 1526 | // when the pointee is zero-sized, we still want to create a pointer. |
| | 1527 | // but instead use a default pointer type as storage. |
| | 1528 | const zero_ptr = try self.allocStack(Type.usize); |
| | 1529 | return zero_ptr; |
| | 1530 | } |
| | 1531 | return self.allocStack(pointee_type); |
| 1493 | } | 1532 | } |
| 1494 | | 1533 | |
| 1495 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1534 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1516,6 +1555,8 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1516,6 +1555,8 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1516 | const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8); | 1555 | const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8); |
| 1517 | const payload_offset = if (ty.zigTypeTag() == .ErrorUnion) | 1556 | const payload_offset = if (ty.zigTypeTag() == .ErrorUnion) |
| 1518 | @intCast(u32, tag_ty.abiSize(self.target)) | 1557 | @intCast(u32, tag_ty.abiSize(self.target)) |
| | 1558 | else if (ty.isPtrLikeOptional()) |
| | 1559 | @as(u32, 0) |
| 1519 | else | 1560 | else |
| 1520 | @intCast(u32, ty.abiSize(self.target) - payload_ty.abiSize(self.target)); | 1561 | @intCast(u32, ty.abiSize(self.target) - payload_ty.abiSize(self.target)); |
| 1521 | | 1562 | |
| ... | @@ -1528,6 +1569,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1528,6 +1569,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1528 | try self.addLabel(.local_set, mem_local.local); | 1569 | try self.addLabel(.local_set, mem_local.local); |
| 1529 | try self.store(lhs, mem_local, ty, 0); | 1570 | try self.store(lhs, mem_local, ty, 0); |
| 1530 | return; | 1571 | return; |
| | 1572 | } else if (ty.isPtrLikeOptional()) { |
| | 1573 | // set the address of rhs to lhs |
| | 1574 | try self.store(lhs, rhs, Type.usize, 0); |
| | 1575 | return; |
| 1531 | } | 1576 | } |
| 1532 | // constant will contain both tag and payload, | 1577 | // constant will contain both tag and payload, |
| 1533 | // so save those in 2 temporary locals before storing them | 1578 | // so save those in 2 temporary locals before storing them |
| ... | @@ -1546,11 +1591,23 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1546,11 +1591,23 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1546 | return; | 1591 | return; |
| 1547 | }, | 1592 | }, |
| 1548 | .local => { | 1593 | .local => { |
| | 1594 | // When the optional is pointer-like, we simply store the pointer |
| | 1595 | // instead. |
| | 1596 | if (ty.isPtrLikeOptional()) { |
| | 1597 | try self.store(lhs, rhs, Type.usize, 0); |
| | 1598 | return; |
| | 1599 | } |
| 1549 | // Load values from `rhs` stack position and store in `lhs` instead | 1600 | // Load values from `rhs` stack position and store in `lhs` instead |
| 1550 | const tag_local = try self.load(rhs, tag_ty, 0); | 1601 | const tag_local = try self.load(rhs, tag_ty, 0); |
| 1551 | if (payload_ty.hasCodeGenBits()) { | 1602 | if (payload_ty.hasCodeGenBits()) { |
| 1552 | const payload_local = try self.load(rhs, payload_ty, payload_offset); | 1603 | if (isByRef(payload_ty)) { |
| 1553 | try self.store(lhs, payload_local, payload_ty, payload_offset); | 1604 | const payload_ptr = try self.buildPointerOffset(rhs, payload_offset); |
| | 1605 | const lhs_payload_ptr = try self.buildPointerOffset(lhs, payload_offset); |
| | 1606 | try self.store(lhs_payload_ptr, payload_ptr, payload_ty, 0); |
| | 1607 | } else { |
| | 1608 | const payload_local = try self.load(rhs, payload_ty, payload_offset); |
| | 1609 | try self.store(lhs, payload_local, payload_ty, payload_offset); |
| | 1610 | } |
| 1554 | } | 1611 | } |
| 1555 | return try self.store(lhs, tag_local, tag_ty, 0); | 1612 | return try self.store(lhs, tag_local, tag_ty, 0); |
| 1556 | }, | 1613 | }, |
| ... | @@ -1593,12 +1650,9 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1593,12 +1650,9 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1593 | const len_offset = self.ptrSize(); | 1650 | const len_offset = self.ptrSize(); |
| 1594 | if (val.castTag(.decl_ref)) |decl| { | 1651 | if (val.castTag(.decl_ref)) |decl| { |
| 1595 | // for decl references we also need to retrieve the length and the original decl's pointer | 1652 | // for decl references we also need to retrieve the length and the original decl's pointer |
| 1596 | try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = Type.@"usize".abiAlignment(self.target) }); | 1653 | try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = self.ptrSize() }); |
| 1597 | try self.addLabel(.memory_address, decl.data.link.wasm.sym_index); | 1654 | try self.addLabel(.memory_address, decl.data.link.wasm.sym_index); |
| 1598 | try self.addMemArg( | 1655 | try self.addMemArg(.i32_load, .{ .offset = len_offset, .alignment = self.ptrSize() }); |
| 1599 | .i32_load, | | |
| 1600 | .{ .offset = len_offset, .alignment = Type.@"usize".abiAlignment(self.target) }, | | |
| 1601 | ); | | |
| 1602 | } | 1656 | } |
| 1603 | try self.addLabel(.local_set, len_local.local); | 1657 | try self.addLabel(.local_set, len_local.local); |
| 1604 | try self.addLabel(.local_set, ptr_local.local); | 1658 | try self.addLabel(.local_set, ptr_local.local); |
| ... | @@ -1630,7 +1684,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1630,7 +1684,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1630 | .ErrorSet, | 1684 | .ErrorSet, |
| 1631 | .Enum, | 1685 | .Enum, |
| 1632 | .Bool, | 1686 | .Bool, |
| 1633 | .ErrorUnion, | | |
| 1634 | => @intCast(u8, ty.abiSize(self.target)), | 1687 | => @intCast(u8, ty.abiSize(self.target)), |
| 1635 | else => @as(u8, 4), | 1688 | else => @as(u8, 4), |
| 1636 | }; | 1689 | }; |
| ... | @@ -1670,7 +1723,9 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | ... | @@ -1670,7 +1723,9 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1670 | // load local's value from memory by its stack position | 1723 | // load local's value from memory by its stack position |
| 1671 | try self.emitWValue(operand); | 1724 | try self.emitWValue(operand); |
| 1672 | // Build the opcode with the right bitsize | 1725 | // Build the opcode with the right bitsize |
| 1673 | const signedness: std.builtin.Signedness = if (ty.isUnsignedInt() or ty.zigTypeTag() == .ErrorSet) | 1726 | const signedness: std.builtin.Signedness = if (ty.isUnsignedInt() or |
| | 1727 | ty.zigTypeTag() == .ErrorSet or |
| | 1728 | ty.zigTypeTag() == .Bool) |
| 1674 | .unsigned | 1729 | .unsigned |
| 1675 | else | 1730 | else |
| 1676 | .signed; | 1731 | .signed; |
| ... | @@ -1684,6 +1739,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | ... | @@ -1684,6 +1739,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1684 | .Bool, | 1739 | .Bool, |
| 1685 | .ErrorUnion, | 1740 | .ErrorUnion, |
| 1686 | => @intCast(u8, ty.abiSize(self.target)), | 1741 | => @intCast(u8, ty.abiSize(self.target)), |
| | 1742 | .Optional => blk: { |
| | 1743 | if (ty.isPtrLikeOptional()) break :blk @intCast(u8, self.ptrSize()); |
| | 1744 | break :blk @intCast(u8, ty.abiSize(self.target)); |
| | 1745 | }, |
| 1687 | else => @as(u8, 4), | 1746 | else => @as(u8, 4), |
| 1688 | }; | 1747 | }; |
| 1689 | | 1748 | |
| ... | @@ -1828,7 +1887,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1828,7 +1887,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1828 | } | 1887 | } |
| 1829 | } else if (val.castTag(.int_u64)) |int_ptr| { | 1888 | } else if (val.castTag(.int_u64)) |int_ptr| { |
| 1830 | try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data))); | 1889 | try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data))); |
| 1831 | } else if (val.tag() == .zero) { | 1890 | } else if (val.tag() == .zero or val.tag() == .null_value) { |
| 1832 | try self.addImm32(0); | 1891 | try self.addImm32(0); |
| 1833 | } else if (val.tag() == .one) { | 1892 | } else if (val.tag() == .one) { |
| 1834 | try self.addImm32(1); | 1893 | try self.addImm32(1); |
| ... | @@ -1886,18 +1945,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1886,18 +1945,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1886 | var buf: Type.Payload.ElemType = undefined; | 1945 | var buf: Type.Payload.ElemType = undefined; |
| 1887 | const payload_type = ty.optionalChild(&buf); | 1946 | const payload_type = ty.optionalChild(&buf); |
| 1888 | if (ty.isPtrLikeOptional()) { | 1947 | if (ty.isPtrLikeOptional()) { |
| 1889 | return self.fail("Wasm TODO: emitConstant for optional pointer", .{}); | 1948 | try self.emitConstant(val, payload_type); |
| | 1949 | return; |
| 1890 | } | 1950 | } |
| 1891 | | 1951 | |
| 1892 | // When constant has value 'null', set is_null local to '1' | 1952 | // When constant has value 'null', set is_null local to '1' |
| 1893 | // and payload to '0' | 1953 | // and payload to '0' |
| 1894 | if (val.castTag(.opt_payload)) |payload| { | 1954 | if (val.castTag(.opt_payload)) |payload| { |
| 1895 | try self.addImm32(0); | 1955 | try self.addImm32(1); |
| 1896 | if (payload_type.hasCodeGenBits()) | 1956 | if (payload_type.hasCodeGenBits()) |
| 1897 | try self.emitConstant(payload.data, payload_type); | 1957 | try self.emitConstant(payload.data, payload_type); |
| 1898 | } else { | 1958 | } else { |
| 1899 | // set null-tag | 1959 | // set null-tag |
| 1900 | try self.addImm32(1); | 1960 | try self.addImm32(0); |
| 1901 | // null-tag is set, so write a '0' const | 1961 | // null-tag is set, so write a '0' const |
| 1902 | try self.addImm32(0); | 1962 | try self.addImm32(0); |
| 1903 | } | 1963 | } |
| ... | @@ -1908,11 +1968,16 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1908,11 +1968,16 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1908 | const result = try self.allocStack(ty); | 1968 | const result = try self.allocStack(ty); |
| 1909 | | 1969 | |
| 1910 | const fields = ty.structFields(); | 1970 | const fields = ty.structFields(); |
| | 1971 | var offset: u32 = 0; |
| 1911 | for (fields.values()) |field, index| { | 1972 | for (fields.values()) |field, index| { |
| | 1973 | if (isByRef(field.ty)) { |
| | 1974 | return self.fail("TODO: emitConstant for struct field type {}\n", .{field.ty}); |
| | 1975 | } |
| 1912 | const tmp = try self.allocLocal(field.ty); | 1976 | const tmp = try self.allocLocal(field.ty); |
| 1913 | try self.emitConstant(struct_data.data[index], field.ty); | 1977 | try self.emitConstant(struct_data.data[index], field.ty); |
| 1914 | try self.addLabel(.local_set, tmp.local); | 1978 | try self.addLabel(.local_set, tmp.local); |
| 1915 | try self.store(result, tmp, field.ty, field.offset); | 1979 | try self.store(result, tmp, field.ty, offset); |
| | 1980 | offset += @intCast(u32, field.ty.abiSize(self.target)); |
| 1916 | } | 1981 | } |
| 1917 | try self.addLabel(.local_get, result.local); | 1982 | try self.addLabel(.local_get, result.local); |
| 1918 | }, | 1983 | }, |
| ... | @@ -1936,10 +2001,15 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void { | ... | @@ -1936,10 +2001,15 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void { |
| 1936 | // validator will not accept it due to out-of-bounds memory access); | 2001 | // validator will not accept it due to out-of-bounds memory access); |
| 1937 | .Array => try self.addImm32(@bitCast(i32, @as(u32, 0xaa))), | 2002 | .Array => try self.addImm32(@bitCast(i32, @as(u32, 0xaa))), |
| 1938 | .Struct => { | 2003 | .Struct => { |
| 1939 | // TODO: Write 0xaa to each field | 2004 | // TODO: Write 0xaa struct's memory |
| 1940 | const result = try self.allocStack(ty); | 2005 | const result = try self.allocStack(ty); |
| 1941 | try self.addLabel(.local_get, result.local); | 2006 | try self.addLabel(.local_get, result.local); |
| 1942 | }, | 2007 | }, |
| | 2008 | .Pointer => switch (self.ptrSize()) { |
| | 2009 | 4 => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))), |
| | 2010 | 8 => try self.addImm64(0xaaaaaaaaaaaaaaaa), |
| | 2011 | else => unreachable, |
| | 2012 | }, |
| 1943 | else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}), | 2013 | else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}), |
| 1944 | } | 2014 | } |
| 1945 | } | 2015 | } |
| ... | @@ -2065,23 +2135,34 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2065,23 +2135,34 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2065 | } | 2135 | } |
| 2066 | | 2136 | |
| 2067 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue { | 2137 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue { |
| 2068 | const data: Air.Inst.Data = self.air.instructions.items(.data)[inst]; | 2138 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2069 | const lhs = self.resolveInst(data.bin_op.lhs); | 2139 | const lhs = self.resolveInst(bin_op.lhs); |
| 2070 | const rhs = self.resolveInst(data.bin_op.rhs); | 2140 | const rhs = self.resolveInst(bin_op.rhs); |
| 2071 | const lhs_ty = self.air.typeOf(data.bin_op.lhs); | 2141 | const operand_ty = self.air.typeOf(bin_op.lhs); |
| 2072 | | 2142 | |
| 2073 | try self.emitWValue(lhs); | 2143 | try self.emitWValue(lhs); |
| 2074 | try self.emitWValue(rhs); | 2144 | try self.emitWValue(rhs); |
| 2075 | | 2145 | |
| | 2146 | if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) { |
| | 2147 | var buf: Type.Payload.ElemType = undefined; |
| | 2148 | const payload_ty = operand_ty.optionalChild(&buf); |
| | 2149 | if (payload_ty.hasCodeGenBits()) { |
| | 2150 | // When we hit this case, we must check the value of optionals |
| | 2151 | // that are not pointers. This means first checking against non-null for |
| | 2152 | // both lhs and rhs, as well as checking the payload are matching of lhs and rhs |
| | 2153 | return self.fail("TODO: Implement airCmp for comparing optionals", .{}); |
| | 2154 | } |
| | 2155 | } |
| | 2156 | |
| 2076 | const signedness: std.builtin.Signedness = blk: { | 2157 | const signedness: std.builtin.Signedness = blk: { |
| 2077 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | 2158 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| 2078 | if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned; | 2159 | if (operand_ty.zigTypeTag() != .Int) break :blk .unsigned; |
| 2079 | | 2160 | |
| 2080 | // incase of an actual integer, we emit the correct signedness | 2161 | // incase of an actual integer, we emit the correct signedness |
| 2081 | break :blk lhs_ty.intInfo(self.target).signedness; | 2162 | break :blk operand_ty.intInfo(self.target).signedness; |
| 2082 | }; | 2163 | }; |
| 2083 | const opcode: wasm.Opcode = buildOpcode(.{ | 2164 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 2084 | .valtype1 = try self.typeToValtype(lhs_ty), | 2165 | .valtype1 = try self.typeToValtype(operand_ty), |
| 2085 | .op = switch (op) { | 2166 | .op = switch (op) { |
| 2086 | .lt => .lt, | 2167 | .lt => .lt, |
| 2087 | .lte => .le, | 2168 | .lte => .le, |
| ... | @@ -2132,7 +2213,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2132,7 +2213,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2132 | try self.addTag(.i32_eq); | 2213 | try self.addTag(.i32_eq); |
| 2133 | | 2214 | |
| 2134 | // save the result in the local | 2215 | // save the result in the local |
| 2135 | const not_tmp = try self.allocLocal(self.air.getRefType(ty_op.ty)); | 2216 | const not_tmp = try self.allocLocal(Type.initTag(.i32)); |
| 2136 | try self.addLabel(.local_set, not_tmp.local); | 2217 | try self.addLabel(.local_set, not_tmp.local); |
| 2137 | return not_tmp; | 2218 | return not_tmp; |
| 2138 | } | 2219 | } |
| ... | @@ -2173,7 +2254,7 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2173,7 +2254,7 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2173 | struct_ty.structFieldType(extra.data.field_index), | 2254 | struct_ty.structFieldType(extra.data.field_index), |
| 2174 | }); | 2255 | }); |
| 2175 | }; | 2256 | }; |
| 2176 | return structFieldPtr(struct_ptr, offset); | 2257 | return self.structFieldPtr(struct_ptr, offset); |
| 2177 | } | 2258 | } |
| 2178 | | 2259 | |
| 2179 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue { | 2260 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue { |
| ... | @@ -2186,10 +2267,10 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr | ... | @@ -2186,10 +2267,10 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr |
| 2186 | field_ty, | 2267 | field_ty, |
| 2187 | }); | 2268 | }); |
| 2188 | }; | 2269 | }; |
| 2189 | return structFieldPtr(struct_ptr, offset); | 2270 | return self.structFieldPtr(struct_ptr, offset); |
| 2190 | } | 2271 | } |
| 2191 | | 2272 | |
| 2192 | fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { | 2273 | fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue { |
| 2193 | var final_offset = offset; | 2274 | var final_offset = offset; |
| 2194 | const local = switch (struct_ptr) { | 2275 | const local = switch (struct_ptr) { |
| 2195 | .local => |local| local, | 2276 | .local => |local| local, |
| ... | @@ -2199,7 +2280,7 @@ fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { | ... | @@ -2199,7 +2280,7 @@ fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { |
| 2199 | }, | 2280 | }, |
| 2200 | else => unreachable, | 2281 | else => unreachable, |
| 2201 | }; | 2282 | }; |
| 2202 | return WValue{ .local_with_offset = .{ .local = local, .offset = final_offset } }; | 2283 | return self.buildPointerOffset(.{ .local = local }, final_offset); |
| 2203 | } | 2284 | } |
| 2204 | | 2285 | |
| 2205 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2286 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2434,24 +2515,13 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2434,24 +2515,13 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2434 | const offset = err_ty.errorUnionSet().abiSize(self.target); | 2515 | const offset = err_ty.errorUnionSet().abiSize(self.target); |
| 2435 | | 2516 | |
| 2436 | const err_union = try self.allocStack(err_ty); | 2517 | const err_union = try self.allocStack(err_ty); |
| 2437 | const to_store = switch (op_ty.zigTypeTag()) { | 2518 | const payload_ptr = try self.buildPointerOffset(err_union, offset); |
| 2438 | // for those types we must load the pointer and then store | 2519 | try self.store(payload_ptr, operand, op_ty, 0); |
| 2439 | // its value | | |
| 2440 | .Pointer, .Optional => blk: { | | |
| 2441 | if (!op_ty.isPtrLikeOptional()) { | | |
| 2442 | return self.fail("TODO: airWrapErrUnionPayload for optional type {}", .{op_ty}); | | |
| 2443 | } | | |
| 2444 | break :blk try self.load(operand, op_ty, 0); | | |
| 2445 | }, | | |
| 2446 | .Int => operand, | | |
| 2447 | else => return self.fail("TODO: airWrapErrUnionPayload for type {}", .{op_ty}), | | |
| 2448 | }; | | |
| 2449 | | | |
| 2450 | try self.store(err_union, to_store, op_ty, @intCast(u32, offset)); | | |
| 2451 | | 2520 | |
| 2452 | // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. | 2521 | // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. |
| 2453 | const tmp_local = try self.allocLocal(err_ty.errorUnionSet()); // locals are '0' by default. | 2522 | try self.addLabel(.local_get, err_union.local); |
| 2454 | try self.store(err_union, tmp_local, err_ty.errorUnionSet(), 0); | 2523 | try self.addImm32(0); |
| | 2524 | try self.addMemArg(.i32_store16, .{ .offset = 0, .alignment = 2 }); |
| 2455 | | 2525 | |
| 2456 | return err_union; | 2526 | return err_union; |
| 2457 | } | 2527 | } |
| ... | @@ -2499,64 +2569,140 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2499,64 +2569,140 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2499 | return result; | 2569 | return result; |
| 2500 | } | 2570 | } |
| 2501 | | 2571 | |
| 2502 | fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { | 2572 | fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!WValue { |
| 2503 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2573 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2504 | const operand = self.resolveInst(un_op); | 2574 | const operand = self.resolveInst(un_op); |
| 2505 | | 2575 | |
| 2506 | const op_ty = self.air.typeOf(un_op); | 2576 | const op_ty = self.air.typeOf(un_op); |
| | 2577 | const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty; |
| 2507 | try self.emitWValue(operand); | 2578 | try self.emitWValue(operand); |
| 2508 | if (!op_ty.isPtrLikeOptional()) { | 2579 | if (!optional_ty.isPtrLikeOptional()) { |
| 2509 | try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 }); | 2580 | var buf: Type.Payload.ElemType = undefined; |
| | 2581 | const payload_ty = optional_ty.optionalChild(&buf); |
| | 2582 | // When payload is zero-bits, we can treat operand as a value, rather than a |
| | 2583 | // stack value |
| | 2584 | if (payload_ty.hasCodeGenBits()) { |
| | 2585 | try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 }); |
| | 2586 | } |
| 2510 | } | 2587 | } |
| 2511 | | 2588 | |
| 2512 | // Compare the error value with '0' | 2589 | // Compare the null value with '0' |
| 2513 | try self.addImm32(0); | 2590 | try self.addImm32(0); |
| 2514 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 2591 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2515 | | 2592 | |
| 2516 | const is_null_tmp = try self.allocLocal(Type.initTag(.u8)); | 2593 | const is_null_tmp = try self.allocLocal(Type.initTag(.i32)); |
| 2517 | try self.addLabel(.local_set, is_null_tmp.local); | 2594 | try self.addLabel(.local_set, is_null_tmp.local); |
| 2518 | return is_null_tmp; | 2595 | return is_null_tmp; |
| 2519 | } | 2596 | } |
| 2520 | | 2597 | |
| 2521 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2598 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2599 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2522 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2600 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2523 | const operand = self.resolveInst(ty_op.operand); | 2601 | const operand = self.resolveInst(ty_op.operand); |
| 2524 | const opt_ty = self.air.typeOf(ty_op.operand); | 2602 | const opt_ty = self.air.typeOf(ty_op.operand); |
| | 2603 | const payload_ty = self.air.typeOfIndex(inst); |
| | 2604 | if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} }; |
| | 2605 | if (opt_ty.isPtrLikeOptional()) return operand; |
| | 2606 | |
| | 2607 | const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); |
| 2525 | | 2608 | |
| 2526 | // For pointers we simply return its stack address, rather than | 2609 | if (isByRef(payload_ty)) { |
| 2527 | // loading its value | 2610 | return self.buildPointerOffset(operand, offset); |
| 2528 | if (opt_ty.zigTypeTag() == .Pointer) { | | |
| 2529 | return WValue{ .local_with_offset = .{ .local = operand.local, .offset = 1 } }; | | |
| 2530 | } | 2611 | } |
| 2531 | | 2612 | |
| 2532 | if (opt_ty.isPtrLikeOptional()) return operand; | 2613 | return self.load(operand, payload_ty, @intCast(u32, offset)); |
| | 2614 | } |
| | 2615 | |
| | 2616 | fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2617 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2618 | |
| | 2619 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 2620 | const operand = self.resolveInst(ty_op.operand); |
| | 2621 | const opt_ty = self.air.typeOf(ty_op.operand).childType(); |
| 2533 | | 2622 | |
| 2534 | var buf: Type.Payload.ElemType = undefined; | 2623 | var buf: Type.Payload.ElemType = undefined; |
| 2535 | const child_ty = opt_ty.optionalChild(&buf); | 2624 | const payload_ty = opt_ty.optionalChild(&buf); |
| 2536 | const offset = opt_ty.abiSize(self.target) - child_ty.abiSize(self.target); | 2625 | if (!payload_ty.hasCodeGenBits() or opt_ty.isPtrLikeOptional()) { |
| | 2626 | return operand; |
| | 2627 | } |
| 2537 | | 2628 | |
| 2538 | return self.load(operand, child_ty, @intCast(u32, offset)); | 2629 | const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); |
| | 2630 | return self.buildPointerOffset(operand, offset); |
| 2539 | } | 2631 | } |
| 2540 | | 2632 | |
| 2541 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2633 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2542 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2634 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2543 | const operand = self.resolveInst(ty_op.operand); | 2635 | const operand = self.resolveInst(ty_op.operand); |
| 2544 | _ = operand; | 2636 | const opt_ty = self.air.typeOf(ty_op.operand).childType(); |
| 2545 | return self.fail("TODO - wasm codegen for optional_payload_ptr_set", .{}); | 2637 | var buf: Type.Payload.ElemType = undefined; |
| | 2638 | const payload_ty = opt_ty.optionalChild(&buf); |
| | 2639 | if (!payload_ty.hasCodeGenBits()) { |
| | 2640 | return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty}); |
| | 2641 | } |
| | 2642 | |
| | 2643 | if (opt_ty.isPtrLikeOptional()) { |
| | 2644 | return operand; |
| | 2645 | } |
| | 2646 | |
| | 2647 | const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { |
| | 2648 | return self.fail("Optional type {} too big to fit into stack frame", .{opt_ty}); |
| | 2649 | }; |
| | 2650 | |
| | 2651 | try self.emitWValue(operand); |
| | 2652 | try self.addImm32(1); |
| | 2653 | try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); |
| | 2654 | |
| | 2655 | return self.buildPointerOffset(operand, offset); |
| 2546 | } | 2656 | } |
| 2547 | | 2657 | |
| 2548 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2658 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2659 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2660 | |
| 2549 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2661 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 2662 | const payload_ty = self.air.typeOf(ty_op.operand); |
| | 2663 | if (!payload_ty.hasCodeGenBits()) { |
| | 2664 | const non_null_bit = try self.allocStack(Type.initTag(.u1)); |
| | 2665 | try self.addLabel(.local_get, non_null_bit.local); |
| | 2666 | try self.addImm32(1); |
| | 2667 | try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); |
| | 2668 | return non_null_bit; |
| | 2669 | } |
| | 2670 | |
| 2550 | const operand = self.resolveInst(ty_op.operand); | 2671 | const operand = self.resolveInst(ty_op.operand); |
| | 2672 | const op_ty = self.air.typeOfIndex(inst); |
| | 2673 | if (op_ty.isPtrLikeOptional()) { |
| | 2674 | return operand; |
| | 2675 | } |
| | 2676 | const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { |
| | 2677 | return self.fail("Optional type {} too big to fit into stack frame", .{op_ty}); |
| | 2678 | }; |
| 2551 | | 2679 | |
| 2552 | const op_ty = self.air.typeOf(ty_op.operand); | 2680 | // Create optional type, set the non-null bit, and store the operand inside the optional type |
| 2553 | const optional_ty = self.air.getRefType(ty_op.ty); | 2681 | const result = try self.allocStack(op_ty); |
| 2554 | const offset = optional_ty.abiSize(self.target) - op_ty.abiSize(self.target); | 2682 | try self.addLabel(.local_get, result.local); |
| | 2683 | try self.addImm32(1); |
| | 2684 | try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); |
| | 2685 | |
| | 2686 | const payload_ptr = try self.buildPointerOffset(result, offset); |
| | 2687 | try self.store(payload_ptr, operand, payload_ty, 0); |
| 2555 | | 2688 | |
| 2556 | return WValue{ .local_with_offset = .{ | 2689 | return result; |
| 2557 | .local = operand.local, | 2690 | } |
| 2558 | .offset = @intCast(u32, offset), | 2691 | |
| 2559 | } }; | 2692 | fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2693 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2694 | |
| | 2695 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| | 2696 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| | 2697 | const lhs = self.resolveInst(bin_op.lhs); |
| | 2698 | const rhs = self.resolveInst(bin_op.rhs); |
| | 2699 | const slice_ty = self.air.typeOfIndex(inst); |
| | 2700 | |
| | 2701 | const slice = try self.allocStack(slice_ty); |
| | 2702 | try self.store(slice, lhs, Type.usize, 0); |
| | 2703 | try self.store(slice, rhs, Type.usize, self.ptrSize()); |
| | 2704 | |
| | 2705 | return slice; |
| 2560 | } | 2706 | } |
| 2561 | | 2707 | |
| 2562 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2708 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2809,3 +2955,71 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -2809,3 +2955,71 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 2809 | try self.addLabel(.local_set, result.local); | 2955 | try self.addLabel(.local_set, result.local); |
| 2810 | return result; | 2956 | return result; |
| 2811 | } | 2957 | } |
| | 2958 | |
| | 2959 | fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2960 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| | 2961 | const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data; |
| | 2962 | |
| | 2963 | const ptr = self.resolveInst(pl_op.operand); |
| | 2964 | const value = self.resolveInst(bin_op.lhs); |
| | 2965 | const len = self.resolveInst(bin_op.rhs); |
| | 2966 | try self.memSet(ptr, len, value); |
| | 2967 | |
| | 2968 | return WValue.none; |
| | 2969 | } |
| | 2970 | |
| | 2971 | /// Sets a region of memory at `ptr` to the value of `value` |
| | 2972 | /// When the user has enabled the bulk_memory feature, we lower |
| | 2973 | /// this to wasm's memset instruction. When the feature is not present, |
| | 2974 | /// we implement it manually. |
| | 2975 | fn memSet(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void { |
| | 2976 | // When bulk_memory is enabled, we lower it to wasm's memset instruction. |
| | 2977 | // If not, we lower it ourselves |
| | 2978 | if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) { |
| | 2979 | try self.emitWValue(ptr); |
| | 2980 | try self.emitWValue(value); |
| | 2981 | try self.emitWValue(len); |
| | 2982 | try self.addExtended(.memory_fill); |
| | 2983 | return; |
| | 2984 | } |
| | 2985 | |
| | 2986 | // TODO: We should probably lower this to a call to compiler_rt |
| | 2987 | // But for now, we implement it manually |
| | 2988 | const offset = try self.allocLocal(Type.usize); // local for counter |
| | 2989 | // outer block to jump to when loop is done |
| | 2990 | try self.startBlock(.block, wasm.block_empty); |
| | 2991 | try self.startBlock(.loop, wasm.block_empty); |
| | 2992 | try self.emitWValue(offset); |
| | 2993 | try self.emitWValue(len); |
| | 2994 | switch (self.ptrSize()) { |
| | 2995 | 4 => try self.addTag(.i32_eq), |
| | 2996 | 8 => try self.addTag(.i64_eq), |
| | 2997 | else => unreachable, |
| | 2998 | } |
| | 2999 | try self.addLabel(.br_if, 1); // jump out of loop into outer block (finished) |
| | 3000 | try self.emitWValue(ptr); |
| | 3001 | try self.emitWValue(offset); |
| | 3002 | switch (self.ptrSize()) { |
| | 3003 | 4 => try self.addTag(.i32_add), |
| | 3004 | 8 => try self.addTag(.i64_add), |
| | 3005 | else => unreachable, |
| | 3006 | } |
| | 3007 | try self.emitWValue(value); |
| | 3008 | const mem_store_op: Mir.Inst.Tag = switch (self.ptrSize()) { |
| | 3009 | 4 => .i32_store8, |
| | 3010 | 8 => .i64_store8, |
| | 3011 | else => unreachable, |
| | 3012 | }; |
| | 3013 | try self.addMemArg(mem_store_op, .{ .offset = 0, .alignment = 1 }); |
| | 3014 | try self.emitWValue(offset); |
| | 3015 | try self.addImm32(1); |
| | 3016 | switch (self.ptrSize()) { |
| | 3017 | 4 => try self.addTag(.i32_add), |
| | 3018 | 8 => try self.addTag(.i64_add), |
| | 3019 | else => unreachable, |
| | 3020 | } |
| | 3021 | try self.addLabel(.local_set, offset.local); |
| | 3022 | try self.addLabel(.br, 0); // jump to start of loop |
| | 3023 | try self.endBlock(); |
| | 3024 | try self.endBlock(); |
| | 3025 | } |