| author | |
| committer | |
| log | 25a01a16e0eb729857f8e9a0a87bd0b8e9da301b |
| tree | 4f6607c2f58751a0e6c8cede44f8a6b784501714 |
| parent | 68e0632aa531b33669bfdda374675aa26e91b40f |
| parent | dd8929738876544d9b83c55df6e2ee3a491298ca |
| signature |
Stage2: floats9 files changed, 459 insertions(+), 90 deletions(-)
build.zig+2| ... | ... | @@ -13,6 +13,7 @@ const InstallDirectoryOptions = std.build.InstallDirectoryOptions; |
| 13 | 13 | pub fn build(b: *Builder) !void { |
| 14 | 14 | b.setPreferredReleaseMode(.ReleaseFast); |
| 15 | 15 | const mode = b.standardReleaseOptions(); |
| 16 | const target = b.standardTargetOptions(.{}); | |
| 16 | 17 | |
| 17 | 18 | var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); |
| 18 | 19 | |
| ... | ... | @@ -54,6 +55,7 @@ pub fn build(b: *Builder) !void { |
| 54 | 55 | if (!only_install_lib_files) { |
| 55 | 56 | var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); |
| 56 | 57 | exe.setBuildMode(mode); |
| 58 | exe.setTarget(target); | |
| 57 | 59 | test_step.dependOn(&exe.step); |
| 58 | 60 | b.default_step.dependOn(&exe.step); |
| 59 | 61 |
src-self-hosted/Module.zig+109-28| ... | ... | @@ -2352,6 +2352,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 2352 | 2352 | .fntype => return self.analyzeInstFnType(scope, old_inst.castTag(.fntype).?), |
| 2353 | 2353 | .intcast => return self.analyzeInstIntCast(scope, old_inst.castTag(.intcast).?), |
| 2354 | 2354 | .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?), |
| 2355 | .floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?), | |
| 2355 | 2356 | .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?), |
| 2356 | 2357 | .add => return self.analyzeInstAdd(scope, old_inst.castTag(.add).?), |
| 2357 | 2358 | .sub => return self.analyzeInstSub(scope, old_inst.castTag(.sub).?), |
| ... | ... | @@ -2796,16 +2797,16 @@ fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPt |
| 2796 | 2797 | } |
| 2797 | 2798 | } |
| 2798 | 2799 | |
| 2799 | fn 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); | |
| 2800 | fn 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); | |
| 2802 | 2803 | |
| 2803 | 2804 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { |
| 2804 | 2805 | .ComptimeInt => true, |
| 2805 | 2806 | .Int => false, |
| 2806 | 2807 | else => return self.fail( |
| 2807 | 2808 | scope, |
| 2808 | intcast.positionals.dest_type.src, | |
| 2809 | inst.positionals.dest_type.src, | |
| 2809 | 2810 | "expected integer type, found '{}'", |
| 2810 | 2811 | .{ |
| 2811 | 2812 | dest_type, |
| ... | ... | @@ -2813,21 +2814,23 @@ fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast) |
| 2813 | 2814 | ), |
| 2814 | 2815 | }; |
| 2815 | 2816 | |
| 2816 | switch (new_inst.ty.zigTypeTag()) { | |
| 2817 | switch (operand.ty.zigTypeTag()) { | |
| 2817 | 2818 | .ComptimeInt, .Int => {}, |
| 2818 | 2819 | else => return self.fail( |
| 2819 | 2820 | scope, |
| 2820 | intcast.positionals.value.src, | |
| 2821 | inst.positionals.operand.src, | |
| 2821 | 2822 | "expected integer type, found '{}'", |
| 2822 | .{new_inst.ty}, | |
| 2823 | .{operand.ty}, | |
| 2823 | 2824 | ), |
| 2824 | 2825 | } |
| 2825 | 2826 | |
| 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'", .{}); | |
| 2828 | 2831 | } |
| 2829 | 2832 | |
| 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", .{}); | |
| 2831 | 2834 | } |
| 2832 | 2835 | |
| 2833 | 2836 | fn 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 |
| 2836 | 2839 | return self.bitcast(scope, dest_type, operand); |
| 2837 | 2840 | } |
| 2838 | 2841 | |
| 2842 | fn 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 | ||
| 2839 | 2878 | fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst { |
| 2840 | 2879 | const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr); |
| 2841 | 2880 | 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 { |
| 3358 | 3397 | return self.bitcast(scope, dest_type, inst); |
| 3359 | 3398 | } |
| 3360 | 3399 | |
| 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 | ||
| 3361 | 3408 | // *[N]T to []T |
| 3362 | 3409 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and |
| 3363 | 3410 | (!inst.ty.pointerIsConst() or dest_type.pointerIsConst())) |
| ... | ... | @@ -3371,31 +3418,65 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { |
| 3371 | 3418 | } |
| 3372 | 3419 | } |
| 3373 | 3420 | |
| 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 | } | |
| 3380 | 3453 | } |
| 3381 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | |
| 3382 | 3454 | } |
| 3383 | 3455 | |
| 3384 | 3456 | // integer widening |
| 3385 | 3457 | if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { |
| 3458 | assert(inst.value() == null); // handled above | |
| 3459 | ||
| 3386 | 3460 | const src_info = inst.ty.intInfo(self.target()); |
| 3387 | 3461 | 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); | |
| 3399 | 3480 | } |
| 3400 | 3481 | } |
| 3401 | 3482 |
src-self-hosted/astgen.zig+56-25| ... | ... | @@ -38,6 +38,8 @@ pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { |
| 38 | 38 | .Period => return field(mod, scope, node.castTag(.Period).?), |
| 39 | 39 | .Deref => return deref(mod, scope, node.castTag(.Deref).?), |
| 40 | 40 | .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), | |
| 41 | 43 | else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}), |
| 42 | 44 | } |
| 43 | 45 | } |
| ... | ... | @@ -323,7 +325,14 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr |
| 323 | 325 | 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type), |
| 324 | 326 | 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type), |
| 325 | 327 | 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 | }, | |
| 327 | 336 | }; |
| 328 | 337 | return mod.addZIRInstConst(scope, src, .{ |
| 329 | 338 | .ty = Type.initTag(.type), |
| ... | ... | @@ -405,6 +414,52 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral |
| 405 | 414 | } |
| 406 | 415 | } |
| 407 | 416 | |
| 417 | fn 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 | ||
| 437 | fn 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 | ||
| 408 | 463 | fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst { |
| 409 | 464 | if (asm_node.outputs.len != 0) { |
| 410 | 465 | return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{}); |
| ... | ... | @@ -534,30 +589,6 @@ fn getSimplePrimitiveValue(name: []const u8) ?TypedValue { |
| 534 | 589 | .val = Value.initTag(tag), |
| 535 | 590 | }; |
| 536 | 591 | } |
| 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 | } | |
| 561 | 592 | return null; |
| 562 | 593 | } |
| 563 | 594 |
src-self-hosted/codegen.zig+20| ... | ... | @@ -459,6 +459,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 459 | 459 | .sub => return self.genSub(inst.castTag(.sub).?), |
| 460 | 460 | .unreach => return MCValue{ .unreach = {} }, |
| 461 | 461 | .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}), | |
| 462 | 482 | } |
| 463 | 483 | } |
| 464 | 484 |
src-self-hosted/ir.zig+4| ... | ... | @@ -71,6 +71,8 @@ pub const Inst = struct { |
| 71 | 71 | sub, |
| 72 | 72 | unreach, |
| 73 | 73 | not, |
| 74 | floatcast, | |
| 75 | intcast, | |
| 74 | 76 | |
| 75 | 77 | /// There is one-to-one correspondence between tag and type for now, |
| 76 | 78 | /// but this will not always be the case. For example, binary operations |
| ... | ... | @@ -89,6 +91,8 @@ pub const Inst = struct { |
| 89 | 91 | .isnonnull, |
| 90 | 92 | .isnull, |
| 91 | 93 | .ptrtoint, |
| 94 | .floatcast, | |
| 95 | .intcast, | |
| 92 | 96 | => UnOp, |
| 93 | 97 | |
| 94 | 98 | .add, |
src-self-hosted/value.zig+211-18| ... | ... | @@ -70,6 +70,7 @@ pub const Value = extern union { |
| 70 | 70 | // After this, the tag requires a payload. |
| 71 | 71 | |
| 72 | 72 | ty, |
| 73 | int_type, | |
| 73 | 74 | int_u64, |
| 74 | 75 | int_i64, |
| 75 | 76 | int_big_positive, |
| ... | ... | @@ -80,6 +81,10 @@ pub const Value = extern union { |
| 80 | 81 | elem_ptr, |
| 81 | 82 | bytes, |
| 82 | 83 | repeated, // the value is a value repeated some number of times |
| 84 | float_16, | |
| 85 | float_32, | |
| 86 | float_64, | |
| 87 | float_128, | |
| 83 | 88 | |
| 84 | 89 | pub const last_no_payload_tag = Tag.bool_false; |
| 85 | 90 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | ... | @@ -174,6 +179,7 @@ pub const Value = extern union { |
| 174 | 179 | }; |
| 175 | 180 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 176 | 181 | }, |
| 182 | .int_type => return self.copyPayloadShallow(allocator, Payload.IntType), | |
| 177 | 183 | .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64), |
| 178 | 184 | .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64), |
| 179 | 185 | .int_big_positive => { |
| ... | ... | @@ -213,6 +219,10 @@ pub const Value = extern union { |
| 213 | 219 | }; |
| 214 | 220 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 215 | 221 | }, |
| 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), | |
| 216 | 226 | } |
| 217 | 227 | } |
| 218 | 228 | |
| ... | ... | @@ -279,6 +289,13 @@ pub const Value = extern union { |
| 279 | 289 | .bool_true => return out_stream.writeAll("true"), |
| 280 | 290 | .bool_false => return out_stream.writeAll("false"), |
| 281 | 291 | .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 | }, | |
| 282 | 299 | .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream), |
| 283 | 300 | .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream), |
| 284 | 301 | .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}), |
| ... | ... | @@ -300,6 +317,10 @@ pub const Value = extern union { |
| 300 | 317 | try out_stream.writeAll("(repeated) "); |
| 301 | 318 | val = val.cast(Payload.Repeated).?.val; |
| 302 | 319 | }, |
| 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}), | |
| 303 | 324 | }; |
| 304 | 325 | } |
| 305 | 326 | |
| ... | ... | @@ -323,6 +344,7 @@ pub const Value = extern union { |
| 323 | 344 | pub fn toType(self: Value) Type { |
| 324 | 345 | return switch (self.tag()) { |
| 325 | 346 | .ty => self.cast(Payload.Ty).?.ty, |
| 347 | .int_type => @panic("TODO int type to type"), | |
| 326 | 348 | |
| 327 | 349 | .u8_type => Type.initTag(.u8), |
| 328 | 350 | .i8_type => Type.initTag(.i8), |
| ... | ... | @@ -380,6 +402,10 @@ pub const Value = extern union { |
| 380 | 402 | .elem_ptr, |
| 381 | 403 | .bytes, |
| 382 | 404 | .repeated, |
| 405 | .float_16, | |
| 406 | .float_32, | |
| 407 | .float_64, | |
| 408 | .float_128, | |
| 383 | 409 | => unreachable, |
| 384 | 410 | }; |
| 385 | 411 | } |
| ... | ... | @@ -388,6 +414,7 @@ pub const Value = extern union { |
| 388 | 414 | pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst { |
| 389 | 415 | switch (self.tag()) { |
| 390 | 416 | .ty, |
| 417 | .int_type, | |
| 391 | 418 | .u8_type, |
| 392 | 419 | .i8_type, |
| 393 | 420 | .u16_type, |
| ... | ... | @@ -435,6 +462,10 @@ pub const Value = extern union { |
| 435 | 462 | .bytes, |
| 436 | 463 | .undef, |
| 437 | 464 | .repeated, |
| 465 | .float_16, | |
| 466 | .float_32, | |
| 467 | .float_64, | |
| 468 | .float_128, | |
| 438 | 469 | => unreachable, |
| 439 | 470 | |
| 440 | 471 | .the_one_possible_value, // An integer with one possible value is always zero. |
| ... | ... | @@ -455,6 +486,7 @@ pub const Value = extern union { |
| 455 | 486 | pub fn toUnsignedInt(self: Value) u64 { |
| 456 | 487 | switch (self.tag()) { |
| 457 | 488 | .ty, |
| 489 | .int_type, | |
| 458 | 490 | .u8_type, |
| 459 | 491 | .i8_type, |
| 460 | 492 | .u16_type, |
| ... | ... | @@ -502,6 +534,10 @@ pub const Value = extern union { |
| 502 | 534 | .bytes, |
| 503 | 535 | .undef, |
| 504 | 536 | .repeated, |
| 537 | .float_16, | |
| 538 | .float_32, | |
| 539 | .float_64, | |
| 540 | .float_128, | |
| 505 | 541 | => unreachable, |
| 506 | 542 | |
| 507 | 543 | .zero, |
| ... | ... | @@ -518,11 +554,38 @@ pub const Value = extern union { |
| 518 | 554 | } |
| 519 | 555 | } |
| 520 | 556 | |
| 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 | ||
| 521 | 583 | /// Asserts the value is an integer and not undefined. |
| 522 | 584 | /// Returns the number of bits the value requires to represent stored in twos complement form. |
| 523 | 585 | pub fn intBitCountTwosComp(self: Value) usize { |
| 524 | 586 | switch (self.tag()) { |
| 525 | 587 | .ty, |
| 588 | .int_type, | |
| 526 | 589 | .u8_type, |
| 527 | 590 | .i8_type, |
| 528 | 591 | .u16_type, |
| ... | ... | @@ -570,6 +633,10 @@ pub const Value = extern union { |
| 570 | 633 | .bytes, |
| 571 | 634 | .undef, |
| 572 | 635 | .repeated, |
| 636 | .float_16, | |
| 637 | .float_32, | |
| 638 | .float_64, | |
| 639 | .float_128, | |
| 573 | 640 | => unreachable, |
| 574 | 641 | |
| 575 | 642 | .the_one_possible_value, // an integer with one possible value is always zero |
| ... | ... | @@ -596,6 +663,7 @@ pub const Value = extern union { |
| 596 | 663 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { |
| 597 | 664 | switch (self.tag()) { |
| 598 | 665 | .ty, |
| 666 | .int_type, | |
| 599 | 667 | .u8_type, |
| 600 | 668 | .i8_type, |
| 601 | 669 | .u16_type, |
| ... | ... | @@ -642,6 +710,10 @@ pub const Value = extern union { |
| 642 | 710 | .elem_ptr, |
| 643 | 711 | .bytes, |
| 644 | 712 | .repeated, |
| 713 | .float_16, | |
| 714 | .float_32, | |
| 715 | .float_64, | |
| 716 | .float_128, | |
| 645 | 717 | => unreachable, |
| 646 | 718 | |
| 647 | 719 | .zero, |
| ... | ... | @@ -701,10 +773,55 @@ pub const Value = extern union { |
| 701 | 773 | } |
| 702 | 774 | } |
| 703 | 775 | |
| 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 | ||
| 704 | 820 | /// Asserts the value is a float |
| 705 | 821 | pub fn floatHasFraction(self: Value) bool { |
| 706 | 822 | return switch (self.tag()) { |
| 707 | 823 | .ty, |
| 824 | .int_type, | |
| 708 | 825 | .u8_type, |
| 709 | 826 | .i8_type, |
| 710 | 827 | .u16_type, |
| ... | ... | @@ -762,12 +879,19 @@ pub const Value = extern union { |
| 762 | 879 | => unreachable, |
| 763 | 880 | |
| 764 | 881 | .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"), | |
| 765 | 888 | }; |
| 766 | 889 | } |
| 767 | 890 | |
| 768 | 891 | pub fn orderAgainstZero(lhs: Value) std.math.Order { |
| 769 | switch (lhs.tag()) { | |
| 892 | return switch (lhs.tag()) { | |
| 770 | 893 | .ty, |
| 894 | .int_type, | |
| 771 | 895 | .u8_type, |
| 772 | 896 | .i8_type, |
| 773 | 897 | .u16_type, |
| ... | ... | @@ -820,27 +944,49 @@ pub const Value = extern union { |
| 820 | 944 | .zero, |
| 821 | 945 | .the_one_possible_value, // an integer with one possible value is always zero |
| 822 | 946 | .bool_false, |
| 823 | => return .eq, | |
| 947 | => .eq, | |
| 824 | 948 | |
| 825 | .bool_true => return .gt, | |
| 949 | .bool_true => .gt, | |
| 826 | 950 | |
| 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 | }; | |
| 832 | 961 | } |
| 833 | 962 | |
| 834 | 963 | /// Asserts the value is comparable. |
| 835 | 964 | pub fn order(lhs: Value, rhs: Value) std.math.Order { |
| 836 | 965 | const lhs_tag = lhs.tag(); |
| 837 | const rhs_tag = lhs.tag(); | |
| 966 | const rhs_tag = rhs.tag(); | |
| 838 | 967 | const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value; |
| 839 | 968 | const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value; |
| 840 | 969 | if (lhs_is_zero) return rhs.orderAgainstZero().invert(); |
| 841 | 970 | if (rhs_is_zero) return lhs.orderAgainstZero(); |
| 842 | 971 | |
| 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 | } | |
| 844 | 990 | |
| 845 | 991 | var lhs_bigint_space: BigIntSpace = undefined; |
| 846 | 992 | var rhs_bigint_space: BigIntSpace = undefined; |
| ... | ... | @@ -864,19 +1010,12 @@ pub const Value = extern union { |
| 864 | 1010 | return compare(a, .eq, b); |
| 865 | 1011 | } |
| 866 | 1012 | |
| 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 | ||
| 875 | 1013 | /// Asserts the value is a pointer and dereferences it. |
| 876 | 1014 | /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis. |
| 877 | 1015 | pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value { |
| 878 | 1016 | return switch (self.tag()) { |
| 879 | 1017 | .ty, |
| 1018 | .int_type, | |
| 880 | 1019 | .u8_type, |
| 881 | 1020 | .i8_type, |
| 882 | 1021 | .u16_type, |
| ... | ... | @@ -928,6 +1067,10 @@ pub const Value = extern union { |
| 928 | 1067 | .bytes, |
| 929 | 1068 | .undef, |
| 930 | 1069 | .repeated, |
| 1070 | .float_16, | |
| 1071 | .float_32, | |
| 1072 | .float_64, | |
| 1073 | .float_128, | |
| 931 | 1074 | => unreachable, |
| 932 | 1075 | |
| 933 | 1076 | .the_one_possible_value => Value.initTag(.the_one_possible_value), |
| ... | ... | @@ -946,6 +1089,7 @@ pub const Value = extern union { |
| 946 | 1089 | pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value { |
| 947 | 1090 | switch (self.tag()) { |
| 948 | 1091 | .ty, |
| 1092 | .int_type, | |
| 949 | 1093 | .u8_type, |
| 950 | 1094 | .i8_type, |
| 951 | 1095 | .u16_type, |
| ... | ... | @@ -999,6 +1143,10 @@ pub const Value = extern union { |
| 999 | 1143 | .elem_ptr, |
| 1000 | 1144 | .ref_val, |
| 1001 | 1145 | .decl_ref, |
| 1146 | .float_16, | |
| 1147 | .float_32, | |
| 1148 | .float_64, | |
| 1149 | .float_128, | |
| 1002 | 1150 | => unreachable, |
| 1003 | 1151 | |
| 1004 | 1152 | .bytes => { |
| ... | ... | @@ -1032,6 +1180,7 @@ pub const Value = extern union { |
| 1032 | 1180 | pub fn isNull(self: Value) bool { |
| 1033 | 1181 | return switch (self.tag()) { |
| 1034 | 1182 | .ty, |
| 1183 | .int_type, | |
| 1035 | 1184 | .u8_type, |
| 1036 | 1185 | .i8_type, |
| 1037 | 1186 | .u16_type, |
| ... | ... | @@ -1085,6 +1234,10 @@ pub const Value = extern union { |
| 1085 | 1234 | .elem_ptr, |
| 1086 | 1235 | .bytes, |
| 1087 | 1236 | .repeated, |
| 1237 | .float_16, | |
| 1238 | .float_32, | |
| 1239 | .float_64, | |
| 1240 | .float_128, | |
| 1088 | 1241 | => false, |
| 1089 | 1242 | |
| 1090 | 1243 | .undef => unreachable, |
| ... | ... | @@ -1092,6 +1245,20 @@ pub const Value = extern union { |
| 1092 | 1245 | }; |
| 1093 | 1246 | } |
| 1094 | 1247 | |
| 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 | ||
| 1095 | 1262 | /// This type is not copyable since it may contain pointers to its inner data. |
| 1096 | 1263 | pub const Payload = struct { |
| 1097 | 1264 | tag: Tag, |
| ... | ... | @@ -1162,12 +1329,38 @@ pub const Value = extern union { |
| 1162 | 1329 | ty: Type, |
| 1163 | 1330 | }; |
| 1164 | 1331 | |
| 1332 | pub const IntType = struct { | |
| 1333 | base: Payload = Payload{ .tag = .int_type }, | |
| 1334 | bits: u16, | |
| 1335 | signed: bool, | |
| 1336 | }; | |
| 1337 | ||
| 1165 | 1338 | pub const Repeated = struct { |
| 1166 | 1339 | base: Payload = Payload{ .tag = .ty }, |
| 1167 | 1340 | /// This value is repeated some number of times. The amount of times to repeat |
| 1168 | 1341 | /// is stored externally. |
| 1169 | 1342 | val: Value, |
| 1170 | 1343 | }; |
| 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 | }; | |
| 1171 | 1364 | }; |
| 1172 | 1365 | |
| 1173 | 1366 | /// Big enough to fit any non-BigInt value |
src-self-hosted/zir.zig+43-17| ... | ... | @@ -76,6 +76,7 @@ pub const Inst = struct { |
| 76 | 76 | primitive, |
| 77 | 77 | intcast, |
| 78 | 78 | bitcast, |
| 79 | floatcast, | |
| 79 | 80 | elemptr, |
| 80 | 81 | add, |
| 81 | 82 | sub, |
| ... | ... | @@ -137,6 +138,7 @@ pub const Inst = struct { |
| 137 | 138 | .fntype => FnType, |
| 138 | 139 | .intcast => IntCast, |
| 139 | 140 | .bitcast => BitCast, |
| 141 | .floatcast => FloatCast, | |
| 140 | 142 | .elemptr => ElemPtr, |
| 141 | 143 | .condbr => CondBr, |
| 142 | 144 | }; |
| ... | ... | @@ -169,6 +171,7 @@ pub const Inst = struct { |
| 169 | 171 | .primitive, |
| 170 | 172 | .intcast, |
| 171 | 173 | .bitcast, |
| 174 | .floatcast, | |
| 172 | 175 | .elemptr, |
| 173 | 176 | .add, |
| 174 | 177 | .sub, |
| ... | ... | @@ -556,19 +559,33 @@ pub const Inst = struct { |
| 556 | 559 | }; |
| 557 | 560 | }; |
| 558 | 561 | |
| 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 | ||
| 559 | 574 | pub const IntCast = struct { |
| 560 | 575 | pub const base_tag = Tag.intcast; |
| 576 | pub const builtin_name = "@intCast"; | |
| 561 | 577 | base: Inst, |
| 562 | 578 | |
| 563 | 579 | positionals: struct { |
| 564 | 580 | dest_type: *Inst, |
| 565 | value: *Inst, | |
| 581 | operand: *Inst, | |
| 566 | 582 | }, |
| 567 | 583 | kw_args: struct {}, |
| 568 | 584 | }; |
| 569 | 585 | |
| 570 | 586 | pub const BitCast = struct { |
| 571 | 587 | pub const base_tag = Tag.bitcast; |
| 588 | pub const builtin_name = "@bitCast"; | |
| 572 | 589 | base: Inst, |
| 573 | 590 | |
| 574 | 591 | positionals: struct { |
| ... | ... | @@ -1618,6 +1635,28 @@ const EmitZIR = struct { |
| 1618 | 1635 | return &new_inst.base; |
| 1619 | 1636 | } |
| 1620 | 1637 | |
| 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 | ||
| 1621 | 1660 | fn emitBody( |
| 1622 | 1661 | self: *EmitZIR, |
| 1623 | 1662 | body: ir.Body, |
| ... | ... | @@ -1652,22 +1691,9 @@ const EmitZIR = struct { |
| 1652 | 1691 | .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt), |
| 1653 | 1692 | .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq), |
| 1654 | 1693 | |
| 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), | |
| 1671 | 1697 | |
| 1672 | 1698 | .block => blk: { |
| 1673 | 1699 | 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 |
| 288 | 288 | static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); |
| 289 | 289 | static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); |
| 290 | 290 | static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field); |
| 291 | static void value_to_bigfloat(BigFloat *out, ZigValue *val); | |
| 291 | 292 | |
| 292 | 293 | #define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) |
| 293 | 294 | #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) { |
| 10930 | 10931 | } |
| 10931 | 10932 | BigFloat op1_big; |
| 10932 | 10933 | 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); | |
| 10935 | 10936 | return bigfloat_cmp(&op1_big, &op2_big); |
| 10936 | 10937 | } |
| 10937 | 10938 |
test/stage1/behavior/floatop.zig+11| ... | ... | @@ -434,6 +434,17 @@ fn testFloatComparisons() void { |
| 434 | 434 | } |
| 435 | 435 | } |
| 436 | 436 | |
| 437 | test "different sized float comparisons" { | |
| 438 | testDifferentSizedFloatComparisons(); | |
| 439 | comptime testDifferentSizedFloatComparisons(); | |
| 440 | } | |
| 441 | ||
| 442 | fn testDifferentSizedFloatComparisons() void { | |
| 443 | var a: f16 = 1; | |
| 444 | var b: f64 = 2; | |
| 445 | expect(a < b); | |
| 446 | } | |
| 447 | ||
| 437 | 448 | // TODO This is waiting on library support for the Windows build (not sure why the other's don't need it) |
| 438 | 449 | //test "@nearbyint" { |
| 439 | 450 | // comptime testNearbyInt(); |