| ... | @@ -417,22 +417,24 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -417,22 +417,24 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 417 | .f64 => return .f64_neg, | 417 | .f64 => return .f64_neg, |
| 418 | }, | 418 | }, |
| 419 | .ceil => switch (args.valtype1.?) { | 419 | .ceil => switch (args.valtype1.?) { |
| 420 | .i32, .i64 => unreachable, | 420 | .i64 => unreachable, |
| | 421 | .i32 => return .f32_ceil, // when valtype is f16, we store it in i32. |
| 421 | .f32 => return .f32_ceil, | 422 | .f32 => return .f32_ceil, |
| 422 | .f64 => return .f64_ceil, | 423 | .f64 => return .f64_ceil, |
| 423 | }, | 424 | }, |
| 424 | .floor => switch (args.valtype1.?) { | 425 | .floor => switch (args.valtype1.?) { |
| 425 | .i32, .i64 => unreachable, | 426 | .i64 => unreachable, |
| | 427 | .i32 => return .f32_floor, // when valtype is f16, we store it in i32. |
| 426 | .f32 => return .f32_floor, | 428 | .f32 => return .f32_floor, |
| 427 | .f64 => return .f64_floor, | 429 | .f64 => return .f64_floor, |
| 428 | }, | 430 | }, |
| 429 | .trunc => switch (args.valtype1.?) { | 431 | .trunc => switch (args.valtype1.?) { |
| 430 | .i32 => switch (args.valtype2.?) { | 432 | .i32 => if (args.valtype2) |valty| switch (valty) { |
| 431 | .i32 => unreachable, | 433 | .i32 => unreachable, |
| 432 | .i64 => unreachable, | 434 | .i64 => unreachable, |
| 433 | .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u, | 435 | .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u, |
| 434 | .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u, | 436 | .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u, |
| 435 | }, | 437 | } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32. |
| 436 | .i64 => unreachable, | 438 | .i64 => unreachable, |
| 437 | .f32 => return .f32_trunc, | 439 | .f32 => return .f32_trunc, |
| 438 | .f64 => return .f64_trunc, | 440 | .f64 => return .f64_trunc, |
| ... | @@ -788,55 +790,53 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue { | ... | @@ -788,55 +790,53 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue { |
| 788 | | 790 | |
| 789 | /// Generates a `wasm.Type` from a given function type. | 791 | /// Generates a `wasm.Type` from a given function type. |
| 790 | /// Memory is owned by the caller. | 792 | /// Memory is owned by the caller. |
| 791 | fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std.Target) !wasm.Type { | 793 | fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []const Type, return_type: Type, target: std.Target) !wasm.Type { |
| 792 | var params = std.ArrayList(wasm.Valtype).init(gpa); | 794 | var temp_params = std.ArrayList(wasm.Valtype).init(gpa); |
| 793 | defer params.deinit(); | 795 | defer temp_params.deinit(); |
| 794 | var returns = std.ArrayList(wasm.Valtype).init(gpa); | 796 | var returns = std.ArrayList(wasm.Valtype).init(gpa); |
| 795 | defer returns.deinit(); | 797 | defer returns.deinit(); |
| 796 | | 798 | |
| 797 | if (firstParamSRet(fn_info.cc, fn_info.return_type, target)) { | 799 | if (firstParamSRet(cc, return_type, target)) { |
| 798 | try params.append(.i32); // memory address is always a 32-bit handle | 800 | try temp_params.append(.i32); // memory address is always a 32-bit handle |
| 799 | } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) { | 801 | } else if (return_type.hasRuntimeBitsIgnoreComptime()) { |
| 800 | if (fn_info.cc == .C) { | 802 | if (cc == .C) { |
| 801 | const res_classes = abi.classifyType(fn_info.return_type, target); | 803 | const res_classes = abi.classifyType(return_type, target); |
| 802 | assert(res_classes[0] == .direct and res_classes[1] == .none); | 804 | assert(res_classes[0] == .direct and res_classes[1] == .none); |
| 803 | const scalar_type = abi.scalarType(fn_info.return_type, target); | 805 | const scalar_type = abi.scalarType(return_type, target); |
| 804 | try returns.append(typeToValtype(scalar_type, target)); | 806 | try returns.append(typeToValtype(scalar_type, target)); |
| 805 | } else { | 807 | } else { |
| 806 | try returns.append(typeToValtype(fn_info.return_type, target)); | 808 | try returns.append(typeToValtype(return_type, target)); |
| 807 | } | 809 | } |
| 808 | } else if (fn_info.return_type.isError()) { | 810 | } else if (return_type.isError()) { |
| 809 | try returns.append(.i32); | 811 | try returns.append(.i32); |
| 810 | } | 812 | } |
| 811 | | 813 | |
| 812 | // param types | 814 | // param types |
| 813 | if (fn_info.param_types.len != 0) { | 815 | for (params) |param_type| { |
| 814 | for (fn_info.param_types) |param_type| { | 816 | if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; |
| 815 | if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; | 817 | |
| 816 | | 818 | switch (cc) { |
| 817 | switch (fn_info.cc) { | 819 | .C => { |
| 818 | .C => { | 820 | const param_classes = abi.classifyType(param_type, target); |
| 819 | const param_classes = abi.classifyType(param_type, target); | 821 | for (param_classes) |class| { |
| 820 | for (param_classes) |class| { | 822 | if (class == .none) continue; |
| 821 | if (class == .none) continue; | 823 | if (class == .direct) { |
| 822 | if (class == .direct) { | 824 | const scalar_type = abi.scalarType(param_type, target); |
| 823 | const scalar_type = abi.scalarType(param_type, target); | 825 | try temp_params.append(typeToValtype(scalar_type, target)); |
| 824 | try params.append(typeToValtype(scalar_type, target)); | 826 | } else { |
| 825 | } else { | 827 | try temp_params.append(typeToValtype(param_type, target)); |
| 826 | try params.append(typeToValtype(param_type, target)); | | |
| 827 | } | | |
| 828 | } | 828 | } |
| 829 | }, | 829 | } |
| 830 | else => if (isByRef(param_type, target)) | 830 | }, |
| 831 | try params.append(.i32) | 831 | else => if (isByRef(param_type, target)) |
| 832 | else | 832 | try temp_params.append(.i32) |
| 833 | try params.append(typeToValtype(param_type, target)), | 833 | else |
| 834 | } | 834 | try temp_params.append(typeToValtype(param_type, target)), |
| 835 | } | 835 | } |
| 836 | } | 836 | } |
| 837 | | 837 | |
| 838 | return wasm.Type{ | 838 | return wasm.Type{ |
| 839 | .params = params.toOwnedSlice(), | 839 | .params = temp_params.toOwnedSlice(), |
| 840 | .returns = returns.toOwnedSlice(), | 840 | .returns = returns.toOwnedSlice(), |
| 841 | }; | 841 | }; |
| 842 | } | 842 | } |
| ... | @@ -877,7 +877,8 @@ pub fn generate( | ... | @@ -877,7 +877,8 @@ pub fn generate( |
| 877 | } | 877 | } |
| 878 | | 878 | |
| 879 | fn genFunc(self: *Self) InnerError!void { | 879 | fn genFunc(self: *Self) InnerError!void { |
| 880 | var func_type = try genFunctype(self.gpa, self.decl.ty.fnInfo(), self.target); | 880 | const fn_info = self.decl.ty.fnInfo(); |
| | 881 | var func_type = try genFunctype(self.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, self.target); |
| 881 | defer func_type.deinit(self.gpa); | 882 | defer func_type.deinit(self.gpa); |
| 882 | self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); | 883 | self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); |
| 883 | | 884 | |
| ... | @@ -1733,7 +1734,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -1733,7 +1734,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1733 | break :blk module.declPtr(func.data.owner_decl); | 1734 | break :blk module.declPtr(func.data.owner_decl); |
| 1734 | } else if (func_val.castTag(.extern_fn)) |extern_fn| { | 1735 | } else if (func_val.castTag(.extern_fn)) |extern_fn| { |
| 1735 | const ext_decl = module.declPtr(extern_fn.data.owner_decl); | 1736 | const ext_decl = module.declPtr(extern_fn.data.owner_decl); |
| 1736 | var func_type = try genFunctype(self.gpa, ext_decl.ty.fnInfo(), self.target); | 1737 | const ext_info = ext_decl.ty.fnInfo(); |
| | 1738 | var func_type = try genFunctype(self.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, self.target); |
| 1737 | defer func_type.deinit(self.gpa); | 1739 | defer func_type.deinit(self.gpa); |
| 1738 | ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); | 1740 | ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); |
| 1739 | try self.bin_file.addOrUpdateImport( | 1741 | try self.bin_file.addOrUpdateImport( |
| ... | @@ -1774,7 +1776,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -1774,7 +1776,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1774 | const operand = try self.resolveInst(pl_op.operand); | 1776 | const operand = try self.resolveInst(pl_op.operand); |
| 1775 | try self.emitWValue(operand); | 1777 | try self.emitWValue(operand); |
| 1776 | | 1778 | |
| 1777 | var fn_type = try genFunctype(self.gpa, fn_ty.fnInfo(), self.target); | 1779 | var fn_type = try genFunctype(self.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, self.target); |
| 1778 | defer fn_type.deinit(self.gpa); | 1780 | defer fn_type.deinit(self.gpa); |
| 1779 | | 1781 | |
| 1780 | const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type); | 1782 | const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type); |
| ... | @@ -4883,12 +4885,38 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -4883,12 +4885,38 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4883 | try self.emitWValue(rem_result); | 4885 | try self.emitWValue(rem_result); |
| 4884 | try self.addTag(.select); | 4886 | try self.addTag(.select); |
| 4885 | } else { | 4887 | } else { |
| 4886 | const div_result = try self.binOp(lhs, rhs, ty, .div); | 4888 | const float_bits = ty.floatBits(self.target); |
| 4887 | try self.emitWValue(div_result); | 4889 | if (float_bits > 64) { |
| 4888 | switch (ty.floatBits(self.target)) { | 4890 | return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{float_bits}); |
| 4889 | 32 => try self.addTag(.f32_floor), | 4891 | } |
| 4890 | 64 => try self.addTag(.f64_floor), | 4892 | const is_f16 = float_bits == 16; |
| 4891 | else => |bit_size| return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{bit_size}), | 4893 | |
| | 4894 | const lhs_operand = if (is_f16) blk: { |
| | 4895 | break :blk try self.fpext(lhs, Type.f16, Type.f32); |
| | 4896 | } else lhs; |
| | 4897 | const rhs_operand = if (is_f16) blk: { |
| | 4898 | break :blk try self.fpext(rhs, Type.f16, Type.f32); |
| | 4899 | } else rhs; |
| | 4900 | |
| | 4901 | try self.emitWValue(lhs_operand); |
| | 4902 | try self.emitWValue(rhs_operand); |
| | 4903 | |
| | 4904 | switch (float_bits) { |
| | 4905 | 16, 32 => { |
| | 4906 | try self.addTag(.f32_div); |
| | 4907 | try self.addTag(.f32_floor); |
| | 4908 | }, |
| | 4909 | 64 => { |
| | 4910 | try self.addTag(.f64_div); |
| | 4911 | try self.addTag(.f64_floor); |
| | 4912 | }, |
| | 4913 | else => unreachable, |
| | 4914 | } |
| | 4915 | |
| | 4916 | if (is_f16) { |
| | 4917 | // we can re-use temporary local |
| | 4918 | try self.addLabel(.local_set, lhs_operand.local); |
| | 4919 | return self.fptrunc(lhs_operand, Type.f32, Type.f16); |
| 4892 | } | 4920 | } |
| 4893 | } | 4921 | } |
| 4894 | | 4922 | |
| ... | @@ -4961,22 +4989,28 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu | ... | @@ -4961,22 +4989,28 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu |
| 4961 | | 4989 | |
| 4962 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4990 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4963 | const ty = self.air.typeOfIndex(inst); | 4991 | const ty = self.air.typeOfIndex(inst); |
| | 4992 | const float_bits = ty.floatBits(self.target); |
| | 4993 | const is_f16 = float_bits == 16; |
| 4964 | | 4994 | |
| 4965 | if (ty.zigTypeTag() == .Vector) { | 4995 | if (ty.zigTypeTag() == .Vector) { |
| 4966 | return self.fail("TODO: Implement `@ceil` for vectors", .{}); | 4996 | return self.fail("TODO: Implement `@ceil` for vectors", .{}); |
| 4967 | } | 4997 | } |
| | 4998 | if (float_bits > 64) { |
| | 4999 | return self.fail("TODO: implement `@ceil`, `@trunc`, `@floor` for floats larger than 64bits", .{}); |
| | 5000 | } |
| 4968 | | 5001 | |
| 4969 | const operand = try self.resolveInst(un_op); | 5002 | const operand = try self.resolveInst(un_op); |
| 4970 | try self.emitWValue(operand); | 5003 | const op_to_lower = if (is_f16) blk: { |
| 4971 | switch (ty.floatBits(self.target)) { | 5004 | break :blk try self.fpext(operand, Type.f16, Type.f32); |
| 4972 | 32, 64 => { | 5005 | } else operand; |
| 4973 | const opcode = buildOpcode(.{ | 5006 | try self.emitWValue(op_to_lower); |
| 4974 | .op = op, | 5007 | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target) }); |
| 4975 | .valtype1 = typeToValtype(ty, self.target), | 5008 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 4976 | }); | 5009 | |
| 4977 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 5010 | if (is_f16) { |
| 4978 | }, | 5011 | // re-use temporary to save locals |
| 4979 | else => |bit_size| return self.fail("TODO: Implement `@ceil` for floats with bitsize {d}", .{bit_size}), | 5012 | try self.addLabel(.local_set, op_to_lower.local); |
| | 5013 | return self.fptrunc(op_to_lower, Type.f32, Type.f16); |
| 4980 | } | 5014 | } |
| 4981 | | 5015 | |
| 4982 | const result = try self.allocLocal(ty); | 5016 | const result = try self.allocLocal(ty); |
| ... | @@ -5212,19 +5246,8 @@ fn callIntrinsic( | ... | @@ -5212,19 +5246,8 @@ fn callIntrinsic( |
| 5212 | return self.fail("Could not find or create global symbol '{s}'", .{@errorName(err)}); | 5246 | return self.fail("Could not find or create global symbol '{s}'", .{@errorName(err)}); |
| 5213 | }; | 5247 | }; |
| 5214 | | 5248 | |
| 5215 | // TODO: have genFunctype accept individual params so we don't, | 5249 | // Always pass over C-ABI |
| 5216 | // need to initialize a fake Fn.Data instance. | 5250 | var func_type = try genFunctype(self.gpa, .C, param_types, return_type, self.target); |
| 5217 | var pt_tmp = try self.gpa.dupe(Type, param_types); | | |
| 5218 | defer self.gpa.free(pt_tmp); | | |
| 5219 | var func_type = try genFunctype(self.gpa, .{ | | |
| 5220 | .param_types = pt_tmp, | | |
| 5221 | .comptime_params = undefined, | | |
| 5222 | .return_type = return_type, | | |
| 5223 | .alignment = 0, | | |
| 5224 | .cc = .C, | | |
| 5225 | .is_var_args = false, | | |
| 5226 | .is_generic = false, | | |
| 5227 | }, self.target); | | |
| 5228 | defer func_type.deinit(self.gpa); | 5251 | defer func_type.deinit(self.gpa); |
| 5229 | const func_type_index = try self.bin_file.putOrGetFuncType(func_type); | 5252 | const func_type_index = try self.bin_file.putOrGetFuncType(func_type); |
| 5230 | try self.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index); | 5253 | try self.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index); |