| ... | @@ -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,19 +1112,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu | ... | @@ -1086,19 +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 | // Check if we store the result as a pointer to the stack rather than | | |
| 1092 | // by value | | |
| 1093 | if (isByRef(ret_ty)) { | | |
| 1094 | if (self.initial_stack_value == .none) try self.initializeStack(); | | |
| 1095 | result.return_value = try self.allocStack(ret_ty); | | |
| 1096 | | | |
| 1097 | // We want to make sure the return value's stack value doesn't get overwritten, | | |
| 1098 | // so set initial stack value to current's position instead. | | |
| 1099 | try self.addLabel(.global_get, 0); | | |
| 1100 | try self.addLabel(.local_set, self.initial_stack_value.local); | | |
| 1101 | } | | |
| 1102 | }, | 1115 | }, |
| 1103 | 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}), |
| 1104 | } | 1117 | } |
| ... | @@ -1323,6 +1336,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1323,6 +1336,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1323 | | 1336 | |
| 1324 | .load => self.airLoad(inst), | 1337 | .load => self.airLoad(inst), |
| 1325 | .loop => self.airLoop(inst), | 1338 | .loop => self.airLoop(inst), |
| | 1339 | .memset => self.airMemset(inst), |
| 1326 | .not => self.airNot(inst), | 1340 | .not => self.airNot(inst), |
| 1327 | .optional_payload => self.airOptionalPayload(inst), | 1341 | .optional_payload => self.airOptionalPayload(inst), |
| 1328 | .optional_payload_ptr => self.airOptionalPayloadPtr(inst), | 1342 | .optional_payload_ptr => self.airOptionalPayloadPtr(inst), |
| ... | @@ -1335,18 +1349,21 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1335,18 +1349,21 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1335 | .ret => self.airRet(inst), | 1349 | .ret => self.airRet(inst), |
| 1336 | .ret_ptr => self.airRetPtr(inst), | 1350 | .ret_ptr => self.airRetPtr(inst), |
| 1337 | .ret_load => self.airRetLoad(inst), | 1351 | .ret_load => self.airRetLoad(inst), |
| | 1352 | |
| 1338 | .slice => self.airSlice(inst), | 1353 | .slice => self.airSlice(inst), |
| 1339 | .slice_len => self.airSliceLen(inst), | 1354 | .slice_len => self.airSliceLen(inst), |
| 1340 | .slice_elem_val => self.airSliceElemVal(inst), | 1355 | .slice_elem_val => self.airSliceElemVal(inst), |
| 1341 | .slice_elem_ptr => self.airSliceElemPtr(inst), | 1356 | .slice_elem_ptr => self.airSliceElemPtr(inst), |
| 1342 | .slice_ptr => self.airSlicePtr(inst), | 1357 | .slice_ptr => self.airSlicePtr(inst), |
| 1343 | .store => self.airStore(inst), | 1358 | .store => self.airStore(inst), |
| | 1359 | |
| 1344 | .struct_field_ptr => self.airStructFieldPtr(inst), | 1360 | .struct_field_ptr => self.airStructFieldPtr(inst), |
| 1345 | .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0), | 1361 | .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0), |
| 1346 | .struct_field_ptr_index_1 => self.airStructFieldPtrIndex(inst, 1), | 1362 | .struct_field_ptr_index_1 => self.airStructFieldPtrIndex(inst, 1), |
| 1347 | .struct_field_ptr_index_2 => self.airStructFieldPtrIndex(inst, 2), | 1363 | .struct_field_ptr_index_2 => self.airStructFieldPtrIndex(inst, 2), |
| 1348 | .struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3), | 1364 | .struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3), |
| 1349 | .struct_field_val => self.airStructFieldVal(inst), | 1365 | .struct_field_val => self.airStructFieldVal(inst), |
| | 1366 | |
| 1350 | .switch_br => self.airSwitchBr(inst), | 1367 | .switch_br => self.airSwitchBr(inst), |
| 1351 | .trunc => self.airTrunc(inst), | 1368 | .trunc => self.airTrunc(inst), |
| 1352 | .unreach => self.airUnreachable(inst), | 1369 | .unreach => self.airUnreachable(inst), |
| ... | @@ -1374,7 +1391,6 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1374,7 +1391,6 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1374 | // to the stack instead | 1391 | // to the stack instead |
| 1375 | if (self.return_value != .none) { | 1392 | if (self.return_value != .none) { |
| 1376 | 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); |
| 1377 | try self.emitWValue(self.return_value); | | |
| 1378 | } else { | 1394 | } else { |
| 1379 | try self.emitWValue(operand); | 1395 | try self.emitWValue(operand); |
| 1380 | } | 1396 | } |
| ... | @@ -1393,6 +1409,9 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1393,6 +1409,9 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1393 | | 1409 | |
| 1394 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; | 1410 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; |
| 1395 | | 1411 | |
| | 1412 | if (isByRef(child_type)) { |
| | 1413 | return self.return_value; |
| | 1414 | } |
| 1396 | return self.allocStack(child_type); | 1415 | return self.allocStack(child_type); |
| 1397 | } | 1416 | } |
| 1398 | | 1417 | |
| ... | @@ -1402,9 +1421,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1402,9 +1421,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1402 | const ret_ty = self.air.typeOf(un_op).childType(); | 1421 | const ret_ty = self.air.typeOf(un_op).childType(); |
| 1403 | if (!ret_ty.hasCodeGenBits()) return WValue.none; | 1422 | if (!ret_ty.hasCodeGenBits()) return WValue.none; |
| 1404 | | 1423 | |
| 1405 | if (isByRef(ret_ty)) { | 1424 | if (!isByRef(ret_ty)) { |
| 1406 | try self.emitWValue(operand); | | |
| 1407 | } else { | | |
| 1408 | const result = try self.load(operand, ret_ty, 0); | 1425 | const result = try self.load(operand, ret_ty, 0); |
| 1409 | try self.emitWValue(result); | 1426 | try self.emitWValue(result); |
| 1410 | } | 1427 | } |
| ... | @@ -1425,6 +1442,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1425,6 +1442,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1425 | .Pointer => ty.childType(), | 1442 | .Pointer => ty.childType(), |
| 1426 | else => unreachable, | 1443 | else => unreachable, |
| 1427 | }; | 1444 | }; |
| | 1445 | const ret_ty = fn_ty.fnReturnType(); |
| | 1446 | const first_param_sret = isByRef(ret_ty); |
| 1428 | | 1447 | |
| 1429 | const target: ?*Decl = blk: { | 1448 | const target: ?*Decl = blk: { |
| 1430 | 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; |
| ... | @@ -1437,6 +1456,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1437,6 +1456,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1437 | 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()}); |
| 1438 | }; | 1457 | }; |
| 1439 | | 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 | |
| 1440 | for (args) |arg| { | 1465 | for (args) |arg| { |
| 1441 | const arg_ref = @intToEnum(Air.Inst.Ref, arg); | 1466 | const arg_ref = @intToEnum(Air.Inst.Ref, arg); |
| 1442 | const arg_val = self.resolveInst(arg_ref); | 1467 | const arg_val = self.resolveInst(arg_ref); |
| ... | @@ -1475,31 +1500,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1475,31 +1500,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1475 | try self.addLabel(.call_indirect, fn_type_index); | 1500 | try self.addLabel(.call_indirect, fn_type_index); |
| 1476 | } | 1501 | } |
| 1477 | | 1502 | |
| 1478 | const ret_ty = fn_ty.fnReturnType(); | 1503 | if (self.liveness.isUnused(inst) or !ret_ty.hasCodeGenBits()) { |
| 1479 | if (!ret_ty.hasCodeGenBits()) return WValue.none; | 1504 | return WValue.none; |
| 1480 | | 1505 | } else if (ret_ty.isNoReturn()) { |
| 1481 | // TODO: Implement this for all aggregate types | 1506 | try self.addTag(.@"unreachable"); |
| 1482 | if (ret_ty.isSlice()) { | 1507 | return WValue.none; |
| 1483 | // first load the values onto the regular stack, before we move the stack pointer | 1508 | } else if (first_param_sret) { |
| 1484 | // to prevent overwriting the return value. | 1509 | return sret; |
| 1485 | const tmp = try self.allocLocal(ret_ty); | 1510 | } else { |
| 1486 | try self.addLabel(.local_set, tmp.local); | 1511 | const result_local = try self.allocLocal(ret_ty); |
| 1487 | const field_ty = Type.@"usize"; | 1512 | try self.addLabel(.local_set, result_local.local); |
| 1488 | const offset = @intCast(u32, field_ty.abiSize(self.target)); | 1513 | return result_local; |
| 1489 | const ptr_local = try self.load(tmp, field_ty, 0); | | |
| 1490 | const len_local = try self.load(tmp, field_ty, offset); | | |
| 1491 | | | |
| 1492 | // As our values are now safe, we reserve space on the virtual stack and | | |
| 1493 | // store the values there. | | |
| 1494 | const result = try self.allocStack(ret_ty); | | |
| 1495 | try self.store(result, ptr_local, field_ty, 0); | | |
| 1496 | try self.store(result, len_local, field_ty, offset); | | |
| 1497 | return result; | | |
| 1498 | } | 1514 | } |
| 1499 | | | |
| 1500 | const result_local = try self.allocLocal(ret_ty); | | |
| 1501 | try self.addLabel(.local_set, result_local.local); | | |
| 1502 | return result_local; | | |
| 1503 | } | 1515 | } |
| 1504 | | 1516 | |
| 1505 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1517 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1989,7 +2001,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void { | ... | @@ -1989,7 +2001,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void { |
| 1989 | // 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); |
| 1990 | .Array => try self.addImm32(@bitCast(i32, @as(u32, 0xaa))), | 2002 | .Array => try self.addImm32(@bitCast(i32, @as(u32, 0xaa))), |
| 1991 | .Struct => { | 2003 | .Struct => { |
| 1992 | // TODO: Write 0xaa to each field | 2004 | // TODO: Write 0xaa struct's memory |
| 1993 | const result = try self.allocStack(ty); | 2005 | const result = try self.allocStack(ty); |
| 1994 | try self.addLabel(.local_get, result.local); | 2006 | try self.addLabel(.local_get, result.local); |
| 1995 | }, | 2007 | }, |
| ... | @@ -2943,3 +2955,71 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -2943,3 +2955,71 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 2943 | try self.addLabel(.local_set, result.local); | 2955 | try self.addLabel(.local_set, result.local); |
| 2944 | return result; | 2956 | return result; |
| 2945 | } | 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 | } |