| ... | @@ -215,8 +215,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -215,8 +215,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 215 | 16 => switch (args.valtype1.?) { | 215 | 16 => switch (args.valtype1.?) { |
| 216 | .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u, | 216 | .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u, |
| 217 | .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u, | 217 | .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u, |
| 218 | .f32 => return .f32_load, | 218 | .f32, .f64 => unreachable, |
| 219 | .f64 => unreachable, | | |
| 220 | }, | 219 | }, |
| 221 | 32 => switch (args.valtype1.?) { | 220 | 32 => switch (args.valtype1.?) { |
| 222 | .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, | 221 | .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, |
| ... | @@ -246,8 +245,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -246,8 +245,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 246 | 16 => switch (args.valtype1.?) { | 245 | 16 => switch (args.valtype1.?) { |
| 247 | .i32 => return .i32_store16, | 246 | .i32 => return .i32_store16, |
| 248 | .i64 => return .i64_store16, | 247 | .i64 => return .i64_store16, |
| 249 | .f32 => return .f32_store, | 248 | .f32, .f64 => unreachable, |
| 250 | .f64 => unreachable, | | |
| 251 | }, | 249 | }, |
| 252 | 32 => switch (args.valtype1.?) { | 250 | 32 => switch (args.valtype1.?) { |
| 253 | .i64 => return .i64_store32, | 251 | .i64 => return .i64_store32, |
| ... | @@ -725,7 +723,8 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype { | ... | @@ -725,7 +723,8 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype { |
| 725 | return switch (ty.zigTypeTag()) { | 723 | return switch (ty.zigTypeTag()) { |
| 726 | .Float => blk: { | 724 | .Float => blk: { |
| 727 | const bits = ty.floatBits(target); | 725 | const bits = ty.floatBits(target); |
| 728 | if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32; | 726 | if (bits == 16) return wasm.Valtype.i32; // stored/loaded as u16 |
| | 727 | if (bits == 32) break :blk wasm.Valtype.f32; |
| 729 | if (bits == 64) break :blk wasm.Valtype.f64; | 728 | if (bits == 64) break :blk wasm.Valtype.f64; |
| 730 | if (bits == 128) break :blk wasm.Valtype.i64; | 729 | if (bits == 128) break :blk wasm.Valtype.i64; |
| 731 | return wasm.Valtype.i32; // represented as pointer to stack | 730 | return wasm.Valtype.i32; // represented as pointer to stack |
| ... | @@ -2013,6 +2012,10 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa | ... | @@ -2013,6 +2012,10 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa |
| 2013 | } | 2012 | } |
| 2014 | } | 2013 | } |
| 2015 | | 2014 | |
| | 2015 | if (ty.isAnyFloat() and ty.floatBits(self.target) == 16) { |
| | 2016 | return self.binOpFloat16(lhs, rhs, op); |
| | 2017 | } |
| | 2018 | |
| 2016 | const opcode: wasm.Opcode = buildOpcode(.{ | 2019 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 2017 | .op = op, | 2020 | .op = op, |
| 2018 | .valtype1 = typeToValtype(ty, self.target), | 2021 | .valtype1 = typeToValtype(ty, self.target), |
| ... | @@ -2029,6 +2032,20 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa | ... | @@ -2029,6 +2032,20 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa |
| 2029 | return bin_local; | 2032 | return bin_local; |
| 2030 | } | 2033 | } |
| 2031 | | 2034 | |
| | 2035 | fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue { |
| | 2036 | const ext_lhs = try self.fpext(lhs, Type.f16, Type.f32); |
| | 2037 | const ext_rhs = try self.fpext(rhs, Type.f16, Type.f32); |
| | 2038 | |
| | 2039 | const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = .f32, .signedness = .unsigned }); |
| | 2040 | try self.emitWValue(ext_lhs); |
| | 2041 | try self.emitWValue(ext_rhs); |
| | 2042 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| | 2043 | |
| | 2044 | // re-use temporary local |
| | 2045 | try self.addLabel(.local_set, ext_lhs.local); |
| | 2046 | return self.fptrunc(ext_lhs, Type.f32, Type.f16); |
| | 2047 | } |
| | 2048 | |
| 2032 | fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | 2049 | fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 2033 | if (ty.intInfo(self.target).bits > 128) { | 2050 | if (ty.intInfo(self.target).bits > 128) { |
| 2034 | return self.fail("TODO: Implement binary operation for big integer", .{}); | 2051 | return self.fail("TODO: Implement binary operation for big integer", .{}); |
| ... | @@ -2310,8 +2327,9 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { | ... | @@ -2310,8 +2327,9 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2310 | }, | 2327 | }, |
| 2311 | .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) }, | 2328 | .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) }, |
| 2312 | .Float => switch (ty.floatBits(self.target)) { | 2329 | .Float => switch (ty.floatBits(self.target)) { |
| 2313 | 0...32 => return WValue{ .float32 = val.toFloat(f32) }, | 2330 | 16 => return WValue{ .imm32 = @bitCast(u16, val.toFloat(f16)) }, |
| 2314 | 33...64 => return WValue{ .float64 = val.toFloat(f64) }, | 2331 | 32 => return WValue{ .float32 = val.toFloat(f32) }, |
| | 2332 | 64 => return WValue{ .float64 = val.toFloat(f64) }, |
| 2315 | else => unreachable, | 2333 | else => unreachable, |
| 2316 | }, | 2334 | }, |
| 2317 | .Pointer => switch (val.tag()) { | 2335 | .Pointer => switch (val.tag()) { |
| ... | @@ -2389,8 +2407,9 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue { | ... | @@ -2389,8 +2407,9 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue { |
| 2389 | else => unreachable, | 2407 | else => unreachable, |
| 2390 | }, | 2408 | }, |
| 2391 | .Float => switch (ty.floatBits(self.target)) { | 2409 | .Float => switch (ty.floatBits(self.target)) { |
| 2392 | 0...32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) }, | 2410 | 16 => return WValue{ .imm32 = 0xaaaaaaaa }, |
| 2393 | 33...64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) }, | 2411 | 32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) }, |
| | 2412 | 64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) }, |
| 2394 | else => unreachable, | 2413 | else => unreachable, |
| 2395 | }, | 2414 | }, |
| 2396 | .Pointer => switch (self.arch()) { | 2415 | .Pointer => switch (self.arch()) { |
| ... | @@ -2562,6 +2581,8 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper | ... | @@ -2562,6 +2581,8 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper |
| 2562 | } | 2581 | } |
| 2563 | } else if (isByRef(ty, self.target)) { | 2582 | } else if (isByRef(ty, self.target)) { |
| 2564 | return self.cmpBigInt(lhs, rhs, ty, op); | 2583 | return self.cmpBigInt(lhs, rhs, ty, op); |
| | 2584 | } else if (ty.isAnyFloat() and ty.floatBits(self.target) == 16) { |
| | 2585 | return self.cmpFloat16(lhs, rhs, op); |
| 2565 | } | 2586 | } |
| 2566 | | 2587 | |
| 2567 | // ensure that when we compare pointers, we emit | 2588 | // ensure that when we compare pointers, we emit |
| ... | @@ -2595,6 +2616,31 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper | ... | @@ -2595,6 +2616,31 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper |
| 2595 | return cmp_tmp; | 2616 | return cmp_tmp; |
| 2596 | } | 2617 | } |
| 2597 | | 2618 | |
| | 2619 | fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperator) InnerError!WValue { |
| | 2620 | const ext_lhs = try self.fpext(lhs, Type.f16, Type.f32); |
| | 2621 | const ext_rhs = try self.fpext(rhs, Type.f16, Type.f32); |
| | 2622 | |
| | 2623 | const opcode: wasm.Opcode = buildOpcode(.{ |
| | 2624 | .op = switch (op) { |
| | 2625 | .lt => .lt, |
| | 2626 | .lte => .le, |
| | 2627 | .eq => .eq, |
| | 2628 | .neq => .ne, |
| | 2629 | .gte => .ge, |
| | 2630 | .gt => .gt, |
| | 2631 | }, |
| | 2632 | .valtype1 = .f32, |
| | 2633 | .signedness = .unsigned, |
| | 2634 | }); |
| | 2635 | try self.emitWValue(ext_lhs); |
| | 2636 | try self.emitWValue(ext_rhs); |
| | 2637 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| | 2638 | |
| | 2639 | const result = try self.allocLocal(Type.initTag(.i32)); // bool is always i32 |
| | 2640 | try self.addLabel(.local_set, result.local); |
| | 2641 | return result; |
| | 2642 | } |
| | 2643 | |
| 2598 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2644 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2599 | _ = inst; | 2645 | _ = inst; |
| 2600 | return self.fail("TODO implement airCmpVector for wasm", .{}); | 2646 | return self.fail("TODO implement airCmpVector for wasm", .{}); |
| ... | @@ -3934,19 +3980,44 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3934,19 +3980,44 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3934 | | 3980 | |
| 3935 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 3981 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3936 | const dest_ty = self.air.typeOfIndex(inst); | 3982 | const dest_ty = self.air.typeOfIndex(inst); |
| 3937 | const dest_bits = dest_ty.floatBits(self.target); | | |
| 3938 | const src_bits = self.air.typeOf(ty_op.operand).floatBits(self.target); | | |
| 3939 | const operand = try self.resolveInst(ty_op.operand); | 3983 | const operand = try self.resolveInst(ty_op.operand); |
| 3940 | | 3984 | |
| 3941 | if (dest_bits == 64 and src_bits == 32) { | 3985 | return self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty); |
| 3942 | const result = try self.allocLocal(dest_ty); | 3986 | } |
| | 3987 | |
| | 3988 | fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| | 3989 | const given_bits = given.floatBits(self.target); |
| | 3990 | const wanted_bits = wanted.floatBits(self.target); |
| | 3991 | |
| | 3992 | if (wanted_bits == 64 and given_bits == 32) { |
| | 3993 | const result = try self.allocLocal(wanted); |
| 3943 | try self.emitWValue(operand); | 3994 | try self.emitWValue(operand); |
| 3944 | try self.addTag(.f64_promote_f32); | 3995 | try self.addTag(.f64_promote_f32); |
| 3945 | try self.addLabel(.local_set, result.local); | 3996 | try self.addLabel(.local_set, result.local); |
| 3946 | return result; | 3997 | return result; |
| | 3998 | } else if (given_bits == 16) { |
| | 3999 | // call __extendhfsf2(f16) f32 |
| | 4000 | const f32_result = try self.callIntrinsic( |
| | 4001 | "__extendhfsf2", |
| | 4002 | &.{Type.f16}, |
| | 4003 | Type.f32, |
| | 4004 | &.{operand}, |
| | 4005 | ); |
| | 4006 | |
| | 4007 | if (wanted_bits == 32) { |
| | 4008 | return f32_result; |
| | 4009 | } |
| | 4010 | if (wanted_bits == 64) { |
| | 4011 | const result = try self.allocLocal(wanted); |
| | 4012 | try self.emitWValue(f32_result); |
| | 4013 | try self.addTag(.f64_promote_f32); |
| | 4014 | try self.addLabel(.local_set, result.local); |
| | 4015 | return result; |
| | 4016 | } |
| | 4017 | return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits}); |
| 3947 | } else { | 4018 | } else { |
| 3948 | // TODO: Emit a call to compiler-rt to extend the float. e.g. __extendhfsf2 | 4019 | // TODO: Emit a call to compiler-rt to extend the float. e.g. __extendhfsf2 |
| 3949 | return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{dest_bits}); | 4020 | return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits}); |
| 3950 | } | 4021 | } |
| 3951 | } | 4022 | } |
| 3952 | | 4023 | |
| ... | @@ -3955,19 +4026,34 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3955,19 +4026,34 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3955 | | 4026 | |
| 3956 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 4027 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3957 | const dest_ty = self.air.typeOfIndex(inst); | 4028 | const dest_ty = self.air.typeOfIndex(inst); |
| 3958 | const dest_bits = dest_ty.floatBits(self.target); | | |
| 3959 | const src_bits = self.air.typeOf(ty_op.operand).floatBits(self.target); | | |
| 3960 | const operand = try self.resolveInst(ty_op.operand); | 4029 | const operand = try self.resolveInst(ty_op.operand); |
| | 4030 | return self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty); |
| | 4031 | } |
| 3961 | | 4032 | |
| 3962 | if (dest_bits == 32 and src_bits == 64) { | 4033 | fn fptrunc(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 3963 | const result = try self.allocLocal(dest_ty); | 4034 | const given_bits = given.floatBits(self.target); |
| | 4035 | const wanted_bits = wanted.floatBits(self.target); |
| | 4036 | |
| | 4037 | if (wanted_bits == 32 and given_bits == 64) { |
| | 4038 | const result = try self.allocLocal(wanted); |
| 3964 | try self.emitWValue(operand); | 4039 | try self.emitWValue(operand); |
| 3965 | try self.addTag(.f32_demote_f64); | 4040 | try self.addTag(.f32_demote_f64); |
| 3966 | try self.addLabel(.local_set, result.local); | 4041 | try self.addLabel(.local_set, result.local); |
| 3967 | return result; | 4042 | return result; |
| | 4043 | } else if (wanted_bits == 16) { |
| | 4044 | const op: WValue = if (given_bits == 64) blk: { |
| | 4045 | const tmp = try self.allocLocal(Type.f32); |
| | 4046 | try self.emitWValue(operand); |
| | 4047 | try self.addTag(.f32_demote_f64); |
| | 4048 | try self.addLabel(.local_set, tmp.local); |
| | 4049 | break :blk tmp; |
| | 4050 | } else operand; |
| | 4051 | |
| | 4052 | // call __truncsfhf2(f32) f16 |
| | 4053 | return self.callIntrinsic("__truncsfhf2", &.{Type.f32}, Type.f16, &.{op}); |
| 3968 | } else { | 4054 | } else { |
| 3969 | // TODO: Emit a call to compiler-rt to trunc the float. e.g. __truncdfhf2 | 4055 | // TODO: Emit a call to compiler-rt to trunc the float. e.g. __truncdfhf2 |
| 3970 | return self.fail("TODO: Implement 'fptrunc' for floats with bitsize: {d}", .{dest_bits}); | 4056 | return self.fail("TODO: Implement 'fptrunc' for floats with bitsize: {d}", .{wanted_bits}); |
| 3971 | } | 4057 | } |
| 3972 | } | 4058 | } |
| 3973 | | 4059 | |