authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-21 22:35:05+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-07-21 22:35:05+00:00
log25a01a16e0eb729857f8e9a0a87bd0b8e9da301b
tree4f6607c2f58751a0e6c8cede44f8a6b784501714
parent68e0632aa531b33669bfdda374675aa26e91b40f
parentdd8929738876544d9b83c55df6e2ee3a491298ca
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5905 from Vexu/stage2-float

Stage2: floats

9 files changed, 459 insertions(+), 90 deletions(-)

build.zig+2
......@@ -13,6 +13,7 @@ const InstallDirectoryOptions = std.build.InstallDirectoryOptions;
1313pub fn build(b: *Builder) !void {
1414 b.setPreferredReleaseMode(.ReleaseFast);
1515 const mode = b.standardReleaseOptions();
16 const target = b.standardTargetOptions(.{});
1617
1718 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
1819
......@@ -54,6 +55,7 @@ pub fn build(b: *Builder) !void {
5455 if (!only_install_lib_files) {
5556 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
5657 exe.setBuildMode(mode);
58 exe.setTarget(target);
5759 test_step.dependOn(&exe.step);
5860 b.default_step.dependOn(&exe.step);
5961
src-self-hosted/Module.zig+109-28
......@@ -2352,6 +2352,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
23522352 .fntype => return self.analyzeInstFnType(scope, old_inst.castTag(.fntype).?),
23532353 .intcast => return self.analyzeInstIntCast(scope, old_inst.castTag(.intcast).?),
23542354 .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?),
2355 .floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?),
23552356 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?),
23562357 .add => return self.analyzeInstAdd(scope, old_inst.castTag(.add).?),
23572358 .sub => return self.analyzeInstSub(scope, old_inst.castTag(.sub).?),
......@@ -2796,16 +2797,16 @@ fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPt
27962797 }
27972798}
27982799
2799fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast) InnerError!*Inst {
2800 const dest_type = try self.resolveType(scope, intcast.positionals.dest_type);
2801 const new_inst = try self.resolveInst(scope, intcast.positionals.value);
2800fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.IntCast) InnerError!*Inst {
2801 const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
2802 const operand = try self.resolveInst(scope, inst.positionals.operand);
28022803
28032804 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
28042805 .ComptimeInt => true,
28052806 .Int => false,
28062807 else => return self.fail(
28072808 scope,
2808 intcast.positionals.dest_type.src,
2809 inst.positionals.dest_type.src,
28092810 "expected integer type, found '{}'",
28102811 .{
28112812 dest_type,
......@@ -2813,21 +2814,23 @@ fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast)
28132814 ),
28142815 };
28152816
2816 switch (new_inst.ty.zigTypeTag()) {
2817 switch (operand.ty.zigTypeTag()) {
28172818 .ComptimeInt, .Int => {},
28182819 else => return self.fail(
28192820 scope,
2820 intcast.positionals.value.src,
2821 inst.positionals.operand.src,
28212822 "expected integer type, found '{}'",
2822 .{new_inst.ty},
2823 .{operand.ty},
28232824 ),
28242825 }
28252826
2826 if (dest_is_comptime_int or new_inst.value() != null) {
2827 return self.coerce(scope, dest_type, new_inst);
2827 if (operand.value() != null) {
2828 return self.coerce(scope, dest_type, operand);
2829 } else if (dest_is_comptime_int) {
2830 return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{});
28282831 }
28292832
2830 return self.fail(scope, intcast.base.src, "TODO implement analyze widen or shorten int", .{});
2833 return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{});
28312834}
28322835
28332836fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BitCast) InnerError!*Inst {
......@@ -2836,6 +2839,42 @@ fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BitCast) Inn
28362839 return self.bitcast(scope, dest_type, operand);
28372840}
28382841
2842fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.FloatCast) InnerError!*Inst {
2843 const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
2844 const operand = try self.resolveInst(scope, inst.positionals.operand);
2845
2846 const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
2847 .ComptimeFloat => true,
2848 .Float => false,
2849 else => return self.fail(
2850 scope,
2851 inst.positionals.dest_type.src,
2852 "expected float type, found '{}'",
2853 .{
2854 dest_type,
2855 },
2856 ),
2857 };
2858
2859 switch (operand.ty.zigTypeTag()) {
2860 .ComptimeFloat, .Float, .ComptimeInt => {},
2861 else => return self.fail(
2862 scope,
2863 inst.positionals.operand.src,
2864 "expected float type, found '{}'",
2865 .{operand.ty},
2866 ),
2867 }
2868
2869 if (operand.value() != null) {
2870 return self.coerce(scope, dest_type, operand);
2871 } else if (dest_is_comptime_float) {
2872 return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{});
2873 }
2874
2875 return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{});
2876}
2877
28392878fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst {
28402879 const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr);
28412880 const uncasted_index = try self.resolveInst(scope, inst.positionals.index);
......@@ -3358,6 +3397,14 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
33583397 return self.bitcast(scope, dest_type, inst);
33593398 }
33603399
3400 // undefined to anything
3401 if (inst.value()) |val| {
3402 if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
3403 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3404 }
3405 }
3406 assert(inst.ty.zigTypeTag() != .Undefined);
3407
33613408 // *[N]T to []T
33623409 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
33633410 (!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))
......@@ -3371,31 +3418,65 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
33713418 }
33723419 }
33733420
3374 // comptime_int to fixed-width integer
3375 if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
3376 // The representation is already correct; we only need to make sure it fits in the destination type.
3377 const val = inst.value().?; // comptime_int always has comptime known value
3378 if (!val.intFitsInType(dest_type, self.target())) {
3379 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
3421 // comptime known number to other number
3422 if (inst.value()) |val| {
3423 const src_zig_tag = inst.ty.zigTypeTag();
3424 const dst_zig_tag = dest_type.zigTypeTag();
3425
3426 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
3427 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
3428 if (val.floatHasFraction()) {
3429 return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
3430 }
3431 return self.fail(scope, inst.src, "TODO float to int", .{});
3432 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
3433 if (!val.intFitsInType(dest_type, self.target())) {
3434 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
3435 }
3436 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3437 }
3438 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
3439 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
3440 const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) {
3441 error.Overflow => return self.fail(
3442 scope,
3443 inst.src,
3444 "cast of value {} to type '{}' loses information",
3445 .{ val, dest_type },
3446 ),
3447 error.OutOfMemory => return error.OutOfMemory,
3448 };
3449 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
3450 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
3451 return self.fail(scope, inst.src, "TODO int to float", .{});
3452 }
33803453 }
3381 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
33823454 }
33833455
33843456 // integer widening
33853457 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
3458 assert(inst.value() == null); // handled above
3459
33863460 const src_info = inst.ty.intInfo(self.target());
33873461 const dst_info = dest_type.intInfo(self.target());
3388 if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) {
3389 if (inst.value()) |val| {
3390 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3391 } else {
3392 return self.fail(scope, inst.src, "TODO implement runtime integer widening ({} to {})", .{
3393 inst.ty,
3394 dest_type,
3395 });
3396 }
3397 } else {
3398 return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
3462 if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or
3463 // small enough unsigned ints can get casted to large enough signed ints
3464 (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits))
3465 {
3466 const b = try self.requireRuntimeBlock(scope, inst.src);
3467 return self.addUnOp(b, inst.src, dest_type, .intcast, inst);
3468 }
3469 }
3470
3471 // float widening
3472 if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
3473 assert(inst.value() == null); // handled above
3474
3475 const src_bits = inst.ty.floatBits(self.target());
3476 const dst_bits = dest_type.floatBits(self.target());
3477 if (dst_bits >= src_bits) {
3478 const b = try self.requireRuntimeBlock(scope, inst.src);
3479 return self.addUnOp(b, inst.src, dest_type, .floatcast, inst);
33993480 }
34003481 }
34013482
src-self-hosted/astgen.zig+56-25
......@@ -38,6 +38,8 @@ pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
3838 .Period => return field(mod, scope, node.castTag(.Period).?),
3939 .Deref => return deref(mod, scope, node.castTag(.Deref).?),
4040 .BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?),
41 .FloatLiteral => return floatLiteral(mod, scope, node.castTag(.FloatLiteral).?),
42 .UndefinedLiteral, .BoolLiteral, .NullLiteral => return primitiveLiteral(mod, scope, node),
4143 else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),
4244 }
4345}
......@@ -323,7 +325,14 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr
323325 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
324326 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
325327 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
326 else => return mod.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}),
328 else => {
329 const int_type_payload = try scope.arena().create(Value.Payload.IntType);
330 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
331 return mod.addZIRInstConst(scope, src, .{
332 .ty = Type.initTag(.comptime_int),
333 .val = Value.initPayload(&int_type_payload.base),
334 });
335 },
327336 };
328337 return mod.addZIRInstConst(scope, src, .{
329338 .ty = Type.initTag(.type),
......@@ -405,6 +414,52 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral
405414 }
406415}
407416
417fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.FloatLiteral) InnerError!*zir.Inst {
418 const arena = scope.arena();
419 const tree = scope.tree();
420 const bytes = tree.tokenSlice(float_lit.token);
421 if (bytes.len > 2 and bytes[1] == 'x') {
422 return mod.failTok(scope, float_lit.token, "TODO hex floats", .{});
423 }
424
425 const val = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
426 error.InvalidCharacter => unreachable, // validated by tokenizer
427 };
428 const float_payload = try arena.create(Value.Payload.Float_128);
429 float_payload.* = .{ .val = val };
430 const src = tree.token_locs[float_lit.token].start;
431 return mod.addZIRInstConst(scope, src, .{
432 .ty = Type.initTag(.comptime_float),
433 .val = Value.initPayload(&float_payload.base),
434 });
435}
436
437fn primitiveLiteral(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
438 const arena = scope.arena();
439 const tree = scope.tree();
440 const src = tree.token_locs[node.firstToken()].start;
441
442 if (node.cast(ast.Node.BoolLiteral)) |bool_node| {
443 return mod.addZIRInstConst(scope, src, .{
444 .ty = Type.initTag(.bool),
445 .val = if (tree.token_ids[bool_node.token] == .Keyword_true)
446 Value.initTag(.bool_true)
447 else
448 Value.initTag(.bool_false),
449 });
450 } else if (node.tag == .UndefinedLiteral) {
451 return mod.addZIRInstConst(scope, src, .{
452 .ty = Type.initTag(.@"undefined"),
453 .val = Value.initTag(.undef),
454 });
455 } else if (node.tag == .NullLiteral) {
456 return mod.addZIRInstConst(scope, src, .{
457 .ty = Type.initTag(.@"null"),
458 .val = Value.initTag(.null_value),
459 });
460 } else unreachable;
461}
462
408463fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
409464 if (asm_node.outputs.len != 0) {
410465 return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
......@@ -534,30 +589,6 @@ fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
534589 .val = Value.initTag(tag),
535590 };
536591 }
537 if (mem.eql(u8, name, "null")) {
538 return TypedValue{
539 .ty = Type.initTag(.@"null"),
540 .val = Value.initTag(.null_value),
541 };
542 }
543 if (mem.eql(u8, name, "undefined")) {
544 return TypedValue{
545 .ty = Type.initTag(.@"undefined"),
546 .val = Value.initTag(.undef),
547 };
548 }
549 if (mem.eql(u8, name, "true")) {
550 return TypedValue{
551 .ty = Type.initTag(.bool),
552 .val = Value.initTag(.bool_true),
553 };
554 }
555 if (mem.eql(u8, name, "false")) {
556 return TypedValue{
557 .ty = Type.initTag(.bool),
558 .val = Value.initTag(.bool_false),
559 };
560 }
561592 return null;
562593}
563594
src-self-hosted/codegen.zig+20
......@@ -459,6 +459,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
459459 .sub => return self.genSub(inst.castTag(.sub).?),
460460 .unreach => return MCValue{ .unreach = {} },
461461 .not => return self.genNot(inst.castTag(.not).?),
462 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
463 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
464 }
465 }
466
467 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
468 // No side effects, so if it's unreferenced, do nothing.
469 if (inst.base.isUnused())
470 return MCValue.dead;
471 switch (arch) {
472 else => return self.fail(inst.base.src, "TODO implement floatCast for {}", .{self.target.cpu.arch}),
473 }
474 }
475
476 fn genIntCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
477 // No side effects, so if it's unreferenced, do nothing.
478 if (inst.base.isUnused())
479 return MCValue.dead;
480 switch (arch) {
481 else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}),
462482 }
463483 }
464484
src-self-hosted/ir.zig+4
......@@ -71,6 +71,8 @@ pub const Inst = struct {
7171 sub,
7272 unreach,
7373 not,
74 floatcast,
75 intcast,
7476
7577 /// There is one-to-one correspondence between tag and type for now,
7678 /// but this will not always be the case. For example, binary operations
......@@ -89,6 +91,8 @@ pub const Inst = struct {
8991 .isnonnull,
9092 .isnull,
9193 .ptrtoint,
94 .floatcast,
95 .intcast,
9296 => UnOp,
9397
9498 .add,
src-self-hosted/value.zig+211-18
......@@ -70,6 +70,7 @@ pub const Value = extern union {
7070 // After this, the tag requires a payload.
7171
7272 ty,
73 int_type,
7374 int_u64,
7475 int_i64,
7576 int_big_positive,
......@@ -80,6 +81,10 @@ pub const Value = extern union {
8081 elem_ptr,
8182 bytes,
8283 repeated, // the value is a value repeated some number of times
84 float_16,
85 float_32,
86 float_64,
87 float_128,
8388
8489 pub const last_no_payload_tag = Tag.bool_false;
8590 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -174,6 +179,7 @@ pub const Value = extern union {
174179 };
175180 return Value{ .ptr_otherwise = &new_payload.base };
176181 },
182 .int_type => return self.copyPayloadShallow(allocator, Payload.IntType),
177183 .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64),
178184 .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64),
179185 .int_big_positive => {
......@@ -213,6 +219,10 @@ pub const Value = extern union {
213219 };
214220 return Value{ .ptr_otherwise = &new_payload.base };
215221 },
222 .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16),
223 .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32),
224 .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64),
225 .float_128 => return self.copyPayloadShallow(allocator, Payload.Float_128),
216226 }
217227 }
218228
......@@ -279,6 +289,13 @@ pub const Value = extern union {
279289 .bool_true => return out_stream.writeAll("true"),
280290 .bool_false => return out_stream.writeAll("false"),
281291 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
292 .int_type => {
293 const int_type = val.cast(Payload.IntType).?;
294 return out_stream.print("{}{}", .{
295 if (int_type.signed) "s" else "u",
296 int_type.bits,
297 });
298 },
282299 .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
283300 .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),
284301 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
......@@ -300,6 +317,10 @@ pub const Value = extern union {
300317 try out_stream.writeAll("(repeated) ");
301318 val = val.cast(Payload.Repeated).?.val;
302319 },
320 .float_16 => return out_stream.print("{}", .{val.cast(Payload.Float_16).?.val}),
321 .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}),
322 .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}),
323 .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}),
303324 };
304325 }
305326
......@@ -323,6 +344,7 @@ pub const Value = extern union {
323344 pub fn toType(self: Value) Type {
324345 return switch (self.tag()) {
325346 .ty => self.cast(Payload.Ty).?.ty,
347 .int_type => @panic("TODO int type to type"),
326348
327349 .u8_type => Type.initTag(.u8),
328350 .i8_type => Type.initTag(.i8),
......@@ -380,6 +402,10 @@ pub const Value = extern union {
380402 .elem_ptr,
381403 .bytes,
382404 .repeated,
405 .float_16,
406 .float_32,
407 .float_64,
408 .float_128,
383409 => unreachable,
384410 };
385411 }
......@@ -388,6 +414,7 @@ pub const Value = extern union {
388414 pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
389415 switch (self.tag()) {
390416 .ty,
417 .int_type,
391418 .u8_type,
392419 .i8_type,
393420 .u16_type,
......@@ -435,6 +462,10 @@ pub const Value = extern union {
435462 .bytes,
436463 .undef,
437464 .repeated,
465 .float_16,
466 .float_32,
467 .float_64,
468 .float_128,
438469 => unreachable,
439470
440471 .the_one_possible_value, // An integer with one possible value is always zero.
......@@ -455,6 +486,7 @@ pub const Value = extern union {
455486 pub fn toUnsignedInt(self: Value) u64 {
456487 switch (self.tag()) {
457488 .ty,
489 .int_type,
458490 .u8_type,
459491 .i8_type,
460492 .u16_type,
......@@ -502,6 +534,10 @@ pub const Value = extern union {
502534 .bytes,
503535 .undef,
504536 .repeated,
537 .float_16,
538 .float_32,
539 .float_64,
540 .float_128,
505541 => unreachable,
506542
507543 .zero,
......@@ -518,11 +554,38 @@ pub const Value = extern union {
518554 }
519555 }
520556
557 pub fn toBool(self: Value) bool {
558 return switch (self.tag()) {
559 .bool_true => true,
560 .bool_false, .zero => false,
561 else => unreachable,
562 };
563 }
564
565 /// Asserts that the value is a float or an integer.
566 pub fn toFloat(self: Value, comptime T: type) T {
567 return switch (self.tag()) {
568 .float_16 => @panic("TODO soft float"),
569 .float_32 => @floatCast(T, self.cast(Payload.Float_32).?.val),
570 .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),
571 .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
572
573 .zero, .the_one_possible_value => 0,
574 .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
575 // .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int),
576 .int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"),
577
578 .int_big_positive, .int_big_negative => @panic("big int to f128"),
579 else => unreachable,
580 };
581 }
582
521583 /// Asserts the value is an integer and not undefined.
522584 /// Returns the number of bits the value requires to represent stored in twos complement form.
523585 pub fn intBitCountTwosComp(self: Value) usize {
524586 switch (self.tag()) {
525587 .ty,
588 .int_type,
526589 .u8_type,
527590 .i8_type,
528591 .u16_type,
......@@ -570,6 +633,10 @@ pub const Value = extern union {
570633 .bytes,
571634 .undef,
572635 .repeated,
636 .float_16,
637 .float_32,
638 .float_64,
639 .float_128,
573640 => unreachable,
574641
575642 .the_one_possible_value, // an integer with one possible value is always zero
......@@ -596,6 +663,7 @@ pub const Value = extern union {
596663 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
597664 switch (self.tag()) {
598665 .ty,
666 .int_type,
599667 .u8_type,
600668 .i8_type,
601669 .u16_type,
......@@ -642,6 +710,10 @@ pub const Value = extern union {
642710 .elem_ptr,
643711 .bytes,
644712 .repeated,
713 .float_16,
714 .float_32,
715 .float_64,
716 .float_128,
645717 => unreachable,
646718
647719 .zero,
......@@ -701,10 +773,55 @@ pub const Value = extern union {
701773 }
702774 }
703775
776 /// Converts an integer or a float to a float.
777 /// Returns `error.Overflow` if the value does not fit in the new type.
778 pub fn floatCast(self: Value, allocator: *Allocator, ty: Type, target: Target) !Value {
779 const dest_bit_count = switch (ty.tag()) {
780 .comptime_float => 128,
781 else => ty.floatBits(target),
782 };
783 switch (dest_bit_count) {
784 16, 32, 64, 128 => {},
785 else => std.debug.panic("TODO float cast bit count {}\n", .{dest_bit_count}),
786 }
787 if (ty.isInt()) {
788 @panic("TODO int to float");
789 }
790
791 switch (dest_bit_count) {
792 16 => {
793 @panic("TODO soft float");
794 // var res_payload = Value.Payload.Float_16{.val = self.toFloat(f16)};
795 // if (!self.eql(Value.initPayload(&res_payload.base)))
796 // return error.Overflow;
797 // return Value.initPayload(&res_payload.base).copy(allocator);
798 },
799 32 => {
800 var res_payload = Value.Payload.Float_32{.val = self.toFloat(f32)};
801 if (!self.eql(Value.initPayload(&res_payload.base)))
802 return error.Overflow;
803 return Value.initPayload(&res_payload.base).copy(allocator);
804 },
805 64 => {
806 var res_payload = Value.Payload.Float_64{.val = self.toFloat(f64)};
807 if (!self.eql(Value.initPayload(&res_payload.base)))
808 return error.Overflow;
809 return Value.initPayload(&res_payload.base).copy(allocator);
810 },
811 128 => {
812 const float_payload = try allocator.create(Value.Payload.Float_128);
813 float_payload.* = .{ .val = self.toFloat(f128) };
814 return Value.initPayload(&float_payload.base);
815 },
816 else => unreachable,
817 }
818 }
819
704820 /// Asserts the value is a float
705821 pub fn floatHasFraction(self: Value) bool {
706822 return switch (self.tag()) {
707823 .ty,
824 .int_type,
708825 .u8_type,
709826 .i8_type,
710827 .u16_type,
......@@ -762,12 +879,19 @@ pub const Value = extern union {
762879 => unreachable,
763880
764881 .zero => false,
882
883 .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0,
884 .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0,
885 .float_64 => @rem(self.cast(Payload.Float_64).?.val, 1) != 0,
886 // .float_128 => @rem(self.cast(Payload.Float_128).?.val, 1) != 0,
887 .float_128 => @panic("TODO lld: error: undefined symbol: fmodl"),
765888 };
766889 }
767890
768891 pub fn orderAgainstZero(lhs: Value) std.math.Order {
769 switch (lhs.tag()) {
892 return switch (lhs.tag()) {
770893 .ty,
894 .int_type,
771895 .u8_type,
772896 .i8_type,
773897 .u16_type,
......@@ -820,27 +944,49 @@ pub const Value = extern union {
820944 .zero,
821945 .the_one_possible_value, // an integer with one possible value is always zero
822946 .bool_false,
823 => return .eq,
947 => .eq,
824948
825 .bool_true => return .gt,
949 .bool_true => .gt,
826950
827 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
828 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
829 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
830 .int_big_negative => return lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
831 }
951 .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
952 .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
953 .int_big_positive => lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
954 .int_big_negative => lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
955
956 .float_16 => std.math.order(lhs.cast(Payload.Float_16).?.val, 0),
957 .float_32 => std.math.order(lhs.cast(Payload.Float_32).?.val, 0),
958 .float_64 => std.math.order(lhs.cast(Payload.Float_64).?.val, 0),
959 .float_128 => std.math.order(lhs.cast(Payload.Float_128).?.val, 0),
960 };
832961 }
833962
834963 /// Asserts the value is comparable.
835964 pub fn order(lhs: Value, rhs: Value) std.math.Order {
836965 const lhs_tag = lhs.tag();
837 const rhs_tag = lhs.tag();
966 const rhs_tag = rhs.tag();
838967 const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value;
839968 const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value;
840969 if (lhs_is_zero) return rhs.orderAgainstZero().invert();
841970 if (rhs_is_zero) return lhs.orderAgainstZero();
842971
843 // TODO floats
972 const lhs_float = lhs.isFloat();
973 const rhs_float = rhs.isFloat();
974 if (lhs_float and rhs_float) {
975 if (lhs_tag == rhs_tag) {
976 return switch (lhs.tag()) {
977 .float_16 => return std.math.order(lhs.cast(Payload.Float_16).?.val, rhs.cast(Payload.Float_16).?.val),
978 .float_32 => return std.math.order(lhs.cast(Payload.Float_32).?.val, rhs.cast(Payload.Float_32).?.val),
979 .float_64 => return std.math.order(lhs.cast(Payload.Float_64).?.val, rhs.cast(Payload.Float_64).?.val),
980 .float_128 => return std.math.order(lhs.cast(Payload.Float_128).?.val, rhs.cast(Payload.Float_128).?.val),
981 else => unreachable,
982 };
983 }
984 }
985 if (lhs_float or rhs_float) {
986 const lhs_f128 = lhs.toFloat(f128);
987 const rhs_f128 = rhs.toFloat(f128);
988 return std.math.order(lhs_f128, rhs_f128);
989 }
844990
845991 var lhs_bigint_space: BigIntSpace = undefined;
846992 var rhs_bigint_space: BigIntSpace = undefined;
......@@ -864,19 +1010,12 @@ pub const Value = extern union {
8641010 return compare(a, .eq, b);
8651011 }
8661012
867 pub fn toBool(self: Value) bool {
868 return switch (self.tag()) {
869 .bool_true => true,
870 .bool_false, .zero => false,
871 else => unreachable,
872 };
873 }
874
8751013 /// Asserts the value is a pointer and dereferences it.
8761014 /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
8771015 pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
8781016 return switch (self.tag()) {
8791017 .ty,
1018 .int_type,
8801019 .u8_type,
8811020 .i8_type,
8821021 .u16_type,
......@@ -928,6 +1067,10 @@ pub const Value = extern union {
9281067 .bytes,
9291068 .undef,
9301069 .repeated,
1070 .float_16,
1071 .float_32,
1072 .float_64,
1073 .float_128,
9311074 => unreachable,
9321075
9331076 .the_one_possible_value => Value.initTag(.the_one_possible_value),
......@@ -946,6 +1089,7 @@ pub const Value = extern union {
9461089 pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value {
9471090 switch (self.tag()) {
9481091 .ty,
1092 .int_type,
9491093 .u8_type,
9501094 .i8_type,
9511095 .u16_type,
......@@ -999,6 +1143,10 @@ pub const Value = extern union {
9991143 .elem_ptr,
10001144 .ref_val,
10011145 .decl_ref,
1146 .float_16,
1147 .float_32,
1148 .float_64,
1149 .float_128,
10021150 => unreachable,
10031151
10041152 .bytes => {
......@@ -1032,6 +1180,7 @@ pub const Value = extern union {
10321180 pub fn isNull(self: Value) bool {
10331181 return switch (self.tag()) {
10341182 .ty,
1183 .int_type,
10351184 .u8_type,
10361185 .i8_type,
10371186 .u16_type,
......@@ -1085,6 +1234,10 @@ pub const Value = extern union {
10851234 .elem_ptr,
10861235 .bytes,
10871236 .repeated,
1237 .float_16,
1238 .float_32,
1239 .float_64,
1240 .float_128,
10881241 => false,
10891242
10901243 .undef => unreachable,
......@@ -1092,6 +1245,20 @@ pub const Value = extern union {
10921245 };
10931246 }
10941247
1248 /// Valid for all types. Asserts the value is not undefined.
1249 pub fn isFloat(self: Value) bool {
1250 return switch (self.tag()) {
1251 .undef => unreachable,
1252
1253 .float_16,
1254 .float_32,
1255 .float_64,
1256 .float_128,
1257 => true,
1258 else => false,
1259 };
1260 }
1261
10951262 /// This type is not copyable since it may contain pointers to its inner data.
10961263 pub const Payload = struct {
10971264 tag: Tag,
......@@ -1162,12 +1329,38 @@ pub const Value = extern union {
11621329 ty: Type,
11631330 };
11641331
1332 pub const IntType = struct {
1333 base: Payload = Payload{ .tag = .int_type },
1334 bits: u16,
1335 signed: bool,
1336 };
1337
11651338 pub const Repeated = struct {
11661339 base: Payload = Payload{ .tag = .ty },
11671340 /// This value is repeated some number of times. The amount of times to repeat
11681341 /// is stored externally.
11691342 val: Value,
11701343 };
1344
1345 pub const Float_16 = struct {
1346 base: Payload = .{ .tag = .float_16 },
1347 val: f16,
1348 };
1349
1350 pub const Float_32 = struct {
1351 base: Payload = .{ .tag = .float_32 },
1352 val: f32,
1353 };
1354
1355 pub const Float_64 = struct {
1356 base: Payload = .{ .tag = .float_64 },
1357 val: f64,
1358 };
1359
1360 pub const Float_128 = struct {
1361 base: Payload = .{ .tag = .float_128 },
1362 val: f128,
1363 };
11711364 };
11721365
11731366 /// Big enough to fit any non-BigInt value
src-self-hosted/zir.zig+43-17
......@@ -76,6 +76,7 @@ pub const Inst = struct {
7676 primitive,
7777 intcast,
7878 bitcast,
79 floatcast,
7980 elemptr,
8081 add,
8182 sub,
......@@ -137,6 +138,7 @@ pub const Inst = struct {
137138 .fntype => FnType,
138139 .intcast => IntCast,
139140 .bitcast => BitCast,
141 .floatcast => FloatCast,
140142 .elemptr => ElemPtr,
141143 .condbr => CondBr,
142144 };
......@@ -169,6 +171,7 @@ pub const Inst = struct {
169171 .primitive,
170172 .intcast,
171173 .bitcast,
174 .floatcast,
172175 .elemptr,
173176 .add,
174177 .sub,
......@@ -556,19 +559,33 @@ pub const Inst = struct {
556559 };
557560 };
558561
562 pub const FloatCast = struct {
563 pub const base_tag = Tag.floatcast;
564 pub const builtin_name = "@floatCast";
565 base: Inst,
566
567 positionals: struct {
568 dest_type: *Inst,
569 operand: *Inst,
570 },
571 kw_args: struct {},
572 };
573
559574 pub const IntCast = struct {
560575 pub const base_tag = Tag.intcast;
576 pub const builtin_name = "@intCast";
561577 base: Inst,
562578
563579 positionals: struct {
564580 dest_type: *Inst,
565 value: *Inst,
581 operand: *Inst,
566582 },
567583 kw_args: struct {},
568584 };
569585
570586 pub const BitCast = struct {
571587 pub const base_tag = Tag.bitcast;
588 pub const builtin_name = "@bitCast";
572589 base: Inst,
573590
574591 positionals: struct {
......@@ -1618,6 +1635,28 @@ const EmitZIR = struct {
16181635 return &new_inst.base;
16191636 }
16201637
1638 fn emitCast(
1639 self: *EmitZIR,
1640 src: usize,
1641 new_body: ZirBody,
1642 old_inst: *ir.Inst.UnOp,
1643 comptime I: type,
1644 ) Allocator.Error!*Inst {
1645 const new_inst = try self.arena.allocator.create(I);
1646 new_inst.* = .{
1647 .base = .{
1648 .src = src,
1649 .tag = I.base_tag,
1650 },
1651 .positionals = .{
1652 .dest_type = (try self.emitType(src, old_inst.base.ty)).inst,
1653 .operand = try self.resolveInst(new_body, old_inst.operand),
1654 },
1655 .kw_args = .{},
1656 };
1657 return &new_inst.base;
1658 }
1659
16211660 fn emitBody(
16221661 self: *EmitZIR,
16231662 body: ir.Body,
......@@ -1652,22 +1691,9 @@ const EmitZIR = struct {
16521691 .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt),
16531692 .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq),
16541693
1655 .bitcast => blk: {
1656 const old_inst = inst.castTag(.bitcast).?;
1657 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1658 new_inst.* = .{
1659 .base = .{
1660 .src = inst.src,
1661 .tag = Inst.BitCast.base_tag,
1662 },
1663 .positionals = .{
1664 .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
1665 .operand = try self.resolveInst(new_body, old_inst.operand),
1666 },
1667 .kw_args = .{},
1668 };
1669 break :blk &new_inst.base;
1670 },
1694 .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, Inst.BitCast),
1695 .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, Inst.IntCast),
1696 .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, Inst.FloatCast),
16711697
16721698 .block => blk: {
16731699 const old_inst = inst.castTag(.block).?;
src/ir.cpp+3-2
......@@ -288,6 +288,7 @@ static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* so
288288static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right);
289289static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right);
290290static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field);
291static void value_to_bigfloat(BigFloat *out, ZigValue *val);
291292
292293#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
293294#define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
......@@ -10930,8 +10931,8 @@ static Cmp float_cmp(ZigValue *op1, ZigValue *op2) {
1093010931 }
1093110932 BigFloat op1_big;
1093210933 BigFloat op2_big;
10933 float_init_bigfloat(op1, &op1_big);
10934 float_init_bigfloat(op2, &op2_big);
10934 value_to_bigfloat(&op1_big, op1);
10935 value_to_bigfloat(&op2_big, op2);
1093510936 return bigfloat_cmp(&op1_big, &op2_big);
1093610937}
1093710938
test/stage1/behavior/floatop.zig+11
......@@ -434,6 +434,17 @@ fn testFloatComparisons() void {
434434 }
435435}
436436
437test "different sized float comparisons" {
438 testDifferentSizedFloatComparisons();
439 comptime testDifferentSizedFloatComparisons();
440}
441
442fn testDifferentSizedFloatComparisons() void {
443 var a: f16 = 1;
444 var b: f64 = 2;
445 expect(a < b);
446}
447
437448// TODO This is waiting on library support for the Windows build (not sure why the other's don't need it)
438449//test "@nearbyint" {
439450// comptime testNearbyInt();