commit
25a01a16e0
|
@ -13,6 +13,7 @@ const InstallDirectoryOptions = std.build.InstallDirectoryOptions;
|
|||
pub fn build(b: *Builder) !void {
|
||||
b.setPreferredReleaseMode(.ReleaseFast);
|
||||
const mode = b.standardReleaseOptions();
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
|
||||
|
||||
|
@ -54,6 +55,7 @@ pub fn build(b: *Builder) !void {
|
|||
if (!only_install_lib_files) {
|
||||
var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
|
||||
exe.setBuildMode(mode);
|
||||
exe.setTarget(target);
|
||||
test_step.dependOn(&exe.step);
|
||||
b.default_step.dependOn(&exe.step);
|
||||
|
||||
|
|
|
@ -2352,6 +2352,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
|
|||
.fntype => return self.analyzeInstFnType(scope, old_inst.castTag(.fntype).?),
|
||||
.intcast => return self.analyzeInstIntCast(scope, old_inst.castTag(.intcast).?),
|
||||
.bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?),
|
||||
.floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?),
|
||||
.elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?),
|
||||
.add => return self.analyzeInstAdd(scope, old_inst.castTag(.add).?),
|
||||
.sub => return self.analyzeInstSub(scope, old_inst.castTag(.sub).?),
|
||||
|
@ -2796,16 +2797,16 @@ fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPt
|
|||
}
|
||||
}
|
||||
|
||||
fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast) InnerError!*Inst {
|
||||
const dest_type = try self.resolveType(scope, intcast.positionals.dest_type);
|
||||
const new_inst = try self.resolveInst(scope, intcast.positionals.value);
|
||||
fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.IntCast) InnerError!*Inst {
|
||||
const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
|
||||
const operand = try self.resolveInst(scope, inst.positionals.operand);
|
||||
|
||||
const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
|
||||
.ComptimeInt => true,
|
||||
.Int => false,
|
||||
else => return self.fail(
|
||||
scope,
|
||||
intcast.positionals.dest_type.src,
|
||||
inst.positionals.dest_type.src,
|
||||
"expected integer type, found '{}'",
|
||||
.{
|
||||
dest_type,
|
||||
|
@ -2813,21 +2814,23 @@ fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast)
|
|||
),
|
||||
};
|
||||
|
||||
switch (new_inst.ty.zigTypeTag()) {
|
||||
switch (operand.ty.zigTypeTag()) {
|
||||
.ComptimeInt, .Int => {},
|
||||
else => return self.fail(
|
||||
scope,
|
||||
intcast.positionals.value.src,
|
||||
inst.positionals.operand.src,
|
||||
"expected integer type, found '{}'",
|
||||
.{new_inst.ty},
|
||||
.{operand.ty},
|
||||
),
|
||||
}
|
||||
|
||||
if (dest_is_comptime_int or new_inst.value() != null) {
|
||||
return self.coerce(scope, dest_type, new_inst);
|
||||
if (operand.value() != null) {
|
||||
return self.coerce(scope, dest_type, operand);
|
||||
} else if (dest_is_comptime_int) {
|
||||
return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{});
|
||||
}
|
||||
|
||||
return self.fail(scope, intcast.base.src, "TODO implement analyze widen or shorten int", .{});
|
||||
return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{});
|
||||
}
|
||||
|
||||
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
|
|||
return self.bitcast(scope, dest_type, operand);
|
||||
}
|
||||
|
||||
fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.FloatCast) InnerError!*Inst {
|
||||
const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
|
||||
const operand = try self.resolveInst(scope, inst.positionals.operand);
|
||||
|
||||
const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
|
||||
.ComptimeFloat => true,
|
||||
.Float => false,
|
||||
else => return self.fail(
|
||||
scope,
|
||||
inst.positionals.dest_type.src,
|
||||
"expected float type, found '{}'",
|
||||
.{
|
||||
dest_type,
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
switch (operand.ty.zigTypeTag()) {
|
||||
.ComptimeFloat, .Float, .ComptimeInt => {},
|
||||
else => return self.fail(
|
||||
scope,
|
||||
inst.positionals.operand.src,
|
||||
"expected float type, found '{}'",
|
||||
.{operand.ty},
|
||||
),
|
||||
}
|
||||
|
||||
if (operand.value() != null) {
|
||||
return self.coerce(scope, dest_type, operand);
|
||||
} else if (dest_is_comptime_float) {
|
||||
return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{});
|
||||
}
|
||||
|
||||
return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{});
|
||||
}
|
||||
|
||||
fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst {
|
||||
const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr);
|
||||
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 {
|
|||
return self.bitcast(scope, dest_type, inst);
|
||||
}
|
||||
|
||||
// undefined to anything
|
||||
if (inst.value()) |val| {
|
||||
if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
|
||||
return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
|
||||
}
|
||||
}
|
||||
assert(inst.ty.zigTypeTag() != .Undefined);
|
||||
|
||||
// *[N]T to []T
|
||||
if (inst.ty.isSinglePointer() and dest_type.isSlice() and
|
||||
(!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))
|
||||
|
@ -3371,31 +3418,65 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
|
|||
}
|
||||
}
|
||||
|
||||
// comptime_int to fixed-width integer
|
||||
if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
|
||||
// The representation is already correct; we only need to make sure it fits in the destination type.
|
||||
const val = inst.value().?; // comptime_int always has comptime known value
|
||||
if (!val.intFitsInType(dest_type, self.target())) {
|
||||
return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
|
||||
// comptime known number to other number
|
||||
if (inst.value()) |val| {
|
||||
const src_zig_tag = inst.ty.zigTypeTag();
|
||||
const dst_zig_tag = dest_type.zigTypeTag();
|
||||
|
||||
if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
|
||||
if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
|
||||
if (val.floatHasFraction()) {
|
||||
return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
|
||||
}
|
||||
return self.fail(scope, inst.src, "TODO float to int", .{});
|
||||
} else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
|
||||
if (!val.intFitsInType(dest_type, self.target())) {
|
||||
return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
|
||||
}
|
||||
return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
|
||||
}
|
||||
} else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
|
||||
if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
|
||||
const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) {
|
||||
error.Overflow => return self.fail(
|
||||
scope,
|
||||
inst.src,
|
||||
"cast of value {} to type '{}' loses information",
|
||||
.{ val, dest_type },
|
||||
),
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
|
||||
} else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
|
||||
return self.fail(scope, inst.src, "TODO int to float", .{});
|
||||
}
|
||||
}
|
||||
return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
|
||||
}
|
||||
|
||||
// integer widening
|
||||
if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
|
||||
assert(inst.value() == null); // handled above
|
||||
|
||||
const src_info = inst.ty.intInfo(self.target());
|
||||
const dst_info = dest_type.intInfo(self.target());
|
||||
if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) {
|
||||
if (inst.value()) |val| {
|
||||
return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
|
||||
} else {
|
||||
return self.fail(scope, inst.src, "TODO implement runtime integer widening ({} to {})", .{
|
||||
inst.ty,
|
||||
dest_type,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
|
||||
if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or
|
||||
// small enough unsigned ints can get casted to large enough signed ints
|
||||
(src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits))
|
||||
{
|
||||
const b = try self.requireRuntimeBlock(scope, inst.src);
|
||||
return self.addUnOp(b, inst.src, dest_type, .intcast, inst);
|
||||
}
|
||||
}
|
||||
|
||||
// float widening
|
||||
if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
|
||||
assert(inst.value() == null); // handled above
|
||||
|
||||
const src_bits = inst.ty.floatBits(self.target());
|
||||
const dst_bits = dest_type.floatBits(self.target());
|
||||
if (dst_bits >= src_bits) {
|
||||
const b = try self.requireRuntimeBlock(scope, inst.src);
|
||||
return self.addUnOp(b, inst.src, dest_type, .floatcast, inst);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
|
|||
.Period => return field(mod, scope, node.castTag(.Period).?),
|
||||
.Deref => return deref(mod, scope, node.castTag(.Deref).?),
|
||||
.BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?),
|
||||
.FloatLiteral => return floatLiteral(mod, scope, node.castTag(.FloatLiteral).?),
|
||||
.UndefinedLiteral, .BoolLiteral, .NullLiteral => return primitiveLiteral(mod, scope, node),
|
||||
else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +325,14 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr
|
|||
16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
|
||||
32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
|
||||
64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
|
||||
else => return mod.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}),
|
||||
else => {
|
||||
const int_type_payload = try scope.arena().create(Value.Payload.IntType);
|
||||
int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
.ty = Type.initTag(.comptime_int),
|
||||
.val = Value.initPayload(&int_type_payload.base),
|
||||
});
|
||||
},
|
||||
};
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
|
@ -405,6 +414,52 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral
|
|||
}
|
||||
}
|
||||
|
||||
fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.FloatLiteral) InnerError!*zir.Inst {
|
||||
const arena = scope.arena();
|
||||
const tree = scope.tree();
|
||||
const bytes = tree.tokenSlice(float_lit.token);
|
||||
if (bytes.len > 2 and bytes[1] == 'x') {
|
||||
return mod.failTok(scope, float_lit.token, "TODO hex floats", .{});
|
||||
}
|
||||
|
||||
const val = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
|
||||
error.InvalidCharacter => unreachable, // validated by tokenizer
|
||||
};
|
||||
const float_payload = try arena.create(Value.Payload.Float_128);
|
||||
float_payload.* = .{ .val = val };
|
||||
const src = tree.token_locs[float_lit.token].start;
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
.ty = Type.initTag(.comptime_float),
|
||||
.val = Value.initPayload(&float_payload.base),
|
||||
});
|
||||
}
|
||||
|
||||
fn primitiveLiteral(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
|
||||
const arena = scope.arena();
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.firstToken()].start;
|
||||
|
||||
if (node.cast(ast.Node.BoolLiteral)) |bool_node| {
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
.ty = Type.initTag(.bool),
|
||||
.val = if (tree.token_ids[bool_node.token] == .Keyword_true)
|
||||
Value.initTag(.bool_true)
|
||||
else
|
||||
Value.initTag(.bool_false),
|
||||
});
|
||||
} else if (node.tag == .UndefinedLiteral) {
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
.ty = Type.initTag(.@"undefined"),
|
||||
.val = Value.initTag(.undef),
|
||||
});
|
||||
} else if (node.tag == .NullLiteral) {
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
.ty = Type.initTag(.@"null"),
|
||||
.val = Value.initTag(.null_value),
|
||||
});
|
||||
} else unreachable;
|
||||
}
|
||||
|
||||
fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
|
||||
if (asm_node.outputs.len != 0) {
|
||||
return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
|
||||
|
@ -534,30 +589,6 @@ fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
|
|||
.val = Value.initTag(tag),
|
||||
};
|
||||
}
|
||||
if (mem.eql(u8, name, "null")) {
|
||||
return TypedValue{
|
||||
.ty = Type.initTag(.@"null"),
|
||||
.val = Value.initTag(.null_value),
|
||||
};
|
||||
}
|
||||
if (mem.eql(u8, name, "undefined")) {
|
||||
return TypedValue{
|
||||
.ty = Type.initTag(.@"undefined"),
|
||||
.val = Value.initTag(.undef),
|
||||
};
|
||||
}
|
||||
if (mem.eql(u8, name, "true")) {
|
||||
return TypedValue{
|
||||
.ty = Type.initTag(.bool),
|
||||
.val = Value.initTag(.bool_true),
|
||||
};
|
||||
}
|
||||
if (mem.eql(u8, name, "false")) {
|
||||
return TypedValue{
|
||||
.ty = Type.initTag(.bool),
|
||||
.val = Value.initTag(.bool_false),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -459,6 +459,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
|||
.sub => return self.genSub(inst.castTag(.sub).?),
|
||||
.unreach => return MCValue{ .unreach = {} },
|
||||
.not => return self.genNot(inst.castTag(.not).?),
|
||||
.floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
|
||||
.intcast => return self.genIntCast(inst.castTag(.intcast).?),
|
||||
}
|
||||
}
|
||||
|
||||
fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
|
||||
// No side effects, so if it's unreferenced, do nothing.
|
||||
if (inst.base.isUnused())
|
||||
return MCValue.dead;
|
||||
switch (arch) {
|
||||
else => return self.fail(inst.base.src, "TODO implement floatCast for {}", .{self.target.cpu.arch}),
|
||||
}
|
||||
}
|
||||
|
||||
fn genIntCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
|
||||
// No side effects, so if it's unreferenced, do nothing.
|
||||
if (inst.base.isUnused())
|
||||
return MCValue.dead;
|
||||
switch (arch) {
|
||||
else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,8 @@ pub const Inst = struct {
|
|||
sub,
|
||||
unreach,
|
||||
not,
|
||||
floatcast,
|
||||
intcast,
|
||||
|
||||
/// There is one-to-one correspondence between tag and type for now,
|
||||
/// but this will not always be the case. For example, binary operations
|
||||
|
@ -89,6 +91,8 @@ pub const Inst = struct {
|
|||
.isnonnull,
|
||||
.isnull,
|
||||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
=> UnOp,
|
||||
|
||||
.add,
|
||||
|
|
|
@ -70,6 +70,7 @@ pub const Value = extern union {
|
|||
// After this, the tag requires a payload.
|
||||
|
||||
ty,
|
||||
int_type,
|
||||
int_u64,
|
||||
int_i64,
|
||||
int_big_positive,
|
||||
|
@ -80,6 +81,10 @@ pub const Value = extern union {
|
|||
elem_ptr,
|
||||
bytes,
|
||||
repeated, // the value is a value repeated some number of times
|
||||
float_16,
|
||||
float_32,
|
||||
float_64,
|
||||
float_128,
|
||||
|
||||
pub const last_no_payload_tag = Tag.bool_false;
|
||||
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
|
||||
|
@ -174,6 +179,7 @@ pub const Value = extern union {
|
|||
};
|
||||
return Value{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
.int_type => return self.copyPayloadShallow(allocator, Payload.IntType),
|
||||
.int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64),
|
||||
.int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64),
|
||||
.int_big_positive => {
|
||||
|
@ -213,6 +219,10 @@ pub const Value = extern union {
|
|||
};
|
||||
return Value{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
.float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16),
|
||||
.float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32),
|
||||
.float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64),
|
||||
.float_128 => return self.copyPayloadShallow(allocator, Payload.Float_128),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,6 +289,13 @@ pub const Value = extern union {
|
|||
.bool_true => return out_stream.writeAll("true"),
|
||||
.bool_false => return out_stream.writeAll("false"),
|
||||
.ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
|
||||
.int_type => {
|
||||
const int_type = val.cast(Payload.IntType).?;
|
||||
return out_stream.print("{}{}", .{
|
||||
if (int_type.signed) "s" else "u",
|
||||
int_type.bits,
|
||||
});
|
||||
},
|
||||
.int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
|
||||
.int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),
|
||||
.int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
|
||||
|
@ -300,6 +317,10 @@ pub const Value = extern union {
|
|||
try out_stream.writeAll("(repeated) ");
|
||||
val = val.cast(Payload.Repeated).?.val;
|
||||
},
|
||||
.float_16 => return out_stream.print("{}", .{val.cast(Payload.Float_16).?.val}),
|
||||
.float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}),
|
||||
.float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}),
|
||||
.float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -323,6 +344,7 @@ pub const Value = extern union {
|
|||
pub fn toType(self: Value) Type {
|
||||
return switch (self.tag()) {
|
||||
.ty => self.cast(Payload.Ty).?.ty,
|
||||
.int_type => @panic("TODO int type to type"),
|
||||
|
||||
.u8_type => Type.initTag(.u8),
|
||||
.i8_type => Type.initTag(.i8),
|
||||
|
@ -380,6 +402,10 @@ pub const Value = extern union {
|
|||
.elem_ptr,
|
||||
.bytes,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
=> unreachable,
|
||||
};
|
||||
}
|
||||
|
@ -388,6 +414,7 @@ pub const Value = extern union {
|
|||
pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
|
||||
switch (self.tag()) {
|
||||
.ty,
|
||||
.int_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
|
@ -435,6 +462,10 @@ pub const Value = extern union {
|
|||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
=> unreachable,
|
||||
|
||||
.the_one_possible_value, // An integer with one possible value is always zero.
|
||||
|
@ -455,6 +486,7 @@ pub const Value = extern union {
|
|||
pub fn toUnsignedInt(self: Value) u64 {
|
||||
switch (self.tag()) {
|
||||
.ty,
|
||||
.int_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
|
@ -502,6 +534,10 @@ pub const Value = extern union {
|
|||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
=> unreachable,
|
||||
|
||||
.zero,
|
||||
|
@ -518,11 +554,38 @@ pub const Value = extern union {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn toBool(self: Value) bool {
|
||||
return switch (self.tag()) {
|
||||
.bool_true => true,
|
||||
.bool_false, .zero => false,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts that the value is a float or an integer.
|
||||
pub fn toFloat(self: Value, comptime T: type) T {
|
||||
return switch (self.tag()) {
|
||||
.float_16 => @panic("TODO soft float"),
|
||||
.float_32 => @floatCast(T, self.cast(Payload.Float_32).?.val),
|
||||
.float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),
|
||||
.float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
|
||||
|
||||
.zero, .the_one_possible_value => 0,
|
||||
.int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
|
||||
// .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int),
|
||||
.int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"),
|
||||
|
||||
.int_big_positive, .int_big_negative => @panic("big int to f128"),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the value is an integer and not undefined.
|
||||
/// Returns the number of bits the value requires to represent stored in twos complement form.
|
||||
pub fn intBitCountTwosComp(self: Value) usize {
|
||||
switch (self.tag()) {
|
||||
.ty,
|
||||
.int_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
|
@ -570,6 +633,10 @@ pub const Value = extern union {
|
|||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
=> unreachable,
|
||||
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
|
@ -596,6 +663,7 @@ pub const Value = extern union {
|
|||
pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
|
||||
switch (self.tag()) {
|
||||
.ty,
|
||||
.int_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
|
@ -642,6 +710,10 @@ pub const Value = extern union {
|
|||
.elem_ptr,
|
||||
.bytes,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
=> unreachable,
|
||||
|
||||
.zero,
|
||||
|
@ -701,10 +773,55 @@ pub const Value = extern union {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts an integer or a float to a float.
|
||||
/// Returns `error.Overflow` if the value does not fit in the new type.
|
||||
pub fn floatCast(self: Value, allocator: *Allocator, ty: Type, target: Target) !Value {
|
||||
const dest_bit_count = switch (ty.tag()) {
|
||||
.comptime_float => 128,
|
||||
else => ty.floatBits(target),
|
||||
};
|
||||
switch (dest_bit_count) {
|
||||
16, 32, 64, 128 => {},
|
||||
else => std.debug.panic("TODO float cast bit count {}\n", .{dest_bit_count}),
|
||||
}
|
||||
if (ty.isInt()) {
|
||||
@panic("TODO int to float");
|
||||
}
|
||||
|
||||
switch (dest_bit_count) {
|
||||
16 => {
|
||||
@panic("TODO soft float");
|
||||
// var res_payload = Value.Payload.Float_16{.val = self.toFloat(f16)};
|
||||
// if (!self.eql(Value.initPayload(&res_payload.base)))
|
||||
// return error.Overflow;
|
||||
// return Value.initPayload(&res_payload.base).copy(allocator);
|
||||
},
|
||||
32 => {
|
||||
var res_payload = Value.Payload.Float_32{.val = self.toFloat(f32)};
|
||||
if (!self.eql(Value.initPayload(&res_payload.base)))
|
||||
return error.Overflow;
|
||||
return Value.initPayload(&res_payload.base).copy(allocator);
|
||||
},
|
||||
64 => {
|
||||
var res_payload = Value.Payload.Float_64{.val = self.toFloat(f64)};
|
||||
if (!self.eql(Value.initPayload(&res_payload.base)))
|
||||
return error.Overflow;
|
||||
return Value.initPayload(&res_payload.base).copy(allocator);
|
||||
},
|
||||
128 => {
|
||||
const float_payload = try allocator.create(Value.Payload.Float_128);
|
||||
float_payload.* = .{ .val = self.toFloat(f128) };
|
||||
return Value.initPayload(&float_payload.base);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts the value is a float
|
||||
pub fn floatHasFraction(self: Value) bool {
|
||||
return switch (self.tag()) {
|
||||
.ty,
|
||||
.int_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
|
@ -762,12 +879,19 @@ pub const Value = extern union {
|
|||
=> unreachable,
|
||||
|
||||
.zero => false,
|
||||
|
||||
.float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0,
|
||||
.float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0,
|
||||
.float_64 => @rem(self.cast(Payload.Float_64).?.val, 1) != 0,
|
||||
// .float_128 => @rem(self.cast(Payload.Float_128).?.val, 1) != 0,
|
||||
.float_128 => @panic("TODO lld: error: undefined symbol: fmodl"),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn orderAgainstZero(lhs: Value) std.math.Order {
|
||||
switch (lhs.tag()) {
|
||||
return switch (lhs.tag()) {
|
||||
.ty,
|
||||
.int_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
|
@ -820,27 +944,49 @@ pub const Value = extern union {
|
|||
.zero,
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
.bool_false,
|
||||
=> return .eq,
|
||||
=> .eq,
|
||||
|
||||
.bool_true => return .gt,
|
||||
.bool_true => .gt,
|
||||
|
||||
.int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
|
||||
.int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
|
||||
.int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
|
||||
.int_big_negative => return lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
|
||||
}
|
||||
.int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
|
||||
.int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
|
||||
.int_big_positive => lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
|
||||
.int_big_negative => lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
|
||||
|
||||
.float_16 => std.math.order(lhs.cast(Payload.Float_16).?.val, 0),
|
||||
.float_32 => std.math.order(lhs.cast(Payload.Float_32).?.val, 0),
|
||||
.float_64 => std.math.order(lhs.cast(Payload.Float_64).?.val, 0),
|
||||
.float_128 => std.math.order(lhs.cast(Payload.Float_128).?.val, 0),
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the value is comparable.
|
||||
pub fn order(lhs: Value, rhs: Value) std.math.Order {
|
||||
const lhs_tag = lhs.tag();
|
||||
const rhs_tag = lhs.tag();
|
||||
const rhs_tag = rhs.tag();
|
||||
const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value;
|
||||
const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value;
|
||||
if (lhs_is_zero) return rhs.orderAgainstZero().invert();
|
||||
if (rhs_is_zero) return lhs.orderAgainstZero();
|
||||
|
||||
// TODO floats
|
||||
const lhs_float = lhs.isFloat();
|
||||
const rhs_float = rhs.isFloat();
|
||||
if (lhs_float and rhs_float) {
|
||||
if (lhs_tag == rhs_tag) {
|
||||
return switch (lhs.tag()) {
|
||||
.float_16 => return std.math.order(lhs.cast(Payload.Float_16).?.val, rhs.cast(Payload.Float_16).?.val),
|
||||
.float_32 => return std.math.order(lhs.cast(Payload.Float_32).?.val, rhs.cast(Payload.Float_32).?.val),
|
||||
.float_64 => return std.math.order(lhs.cast(Payload.Float_64).?.val, rhs.cast(Payload.Float_64).?.val),
|
||||
.float_128 => return std.math.order(lhs.cast(Payload.Float_128).?.val, rhs.cast(Payload.Float_128).?.val),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
}
|
||||
if (lhs_float or rhs_float) {
|
||||
const lhs_f128 = lhs.toFloat(f128);
|
||||
const rhs_f128 = rhs.toFloat(f128);
|
||||
return std.math.order(lhs_f128, rhs_f128);
|
||||
}
|
||||
|
||||
var lhs_bigint_space: BigIntSpace = undefined;
|
||||
var rhs_bigint_space: BigIntSpace = undefined;
|
||||
|
@ -864,19 +1010,12 @@ pub const Value = extern union {
|
|||
return compare(a, .eq, b);
|
||||
}
|
||||
|
||||
pub fn toBool(self: Value) bool {
|
||||
return switch (self.tag()) {
|
||||
.bool_true => true,
|
||||
.bool_false, .zero => false,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the value is a pointer and dereferences it.
|
||||
/// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
|
||||
pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
|
||||
return switch (self.tag()) {
|
||||
.ty,
|
||||
.int_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
|
@ -928,6 +1067,10 @@ pub const Value = extern union {
|
|||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
=> unreachable,
|
||||
|
||||
.the_one_possible_value => Value.initTag(.the_one_possible_value),
|
||||
|
@ -946,6 +1089,7 @@ pub const Value = extern union {
|
|||
pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value {
|
||||
switch (self.tag()) {
|
||||
.ty,
|
||||
.int_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
|
@ -999,6 +1143,10 @@ pub const Value = extern union {
|
|||
.elem_ptr,
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
=> unreachable,
|
||||
|
||||
.bytes => {
|
||||
|
@ -1032,6 +1180,7 @@ pub const Value = extern union {
|
|||
pub fn isNull(self: Value) bool {
|
||||
return switch (self.tag()) {
|
||||
.ty,
|
||||
.int_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
|
@ -1085,6 +1234,10 @@ pub const Value = extern union {
|
|||
.elem_ptr,
|
||||
.bytes,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
=> false,
|
||||
|
||||
.undef => unreachable,
|
||||
|
@ -1092,6 +1245,20 @@ pub const Value = extern union {
|
|||
};
|
||||
}
|
||||
|
||||
/// Valid for all types. Asserts the value is not undefined.
|
||||
pub fn isFloat(self: Value) bool {
|
||||
return switch (self.tag()) {
|
||||
.undef => unreachable,
|
||||
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
=> true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// This type is not copyable since it may contain pointers to its inner data.
|
||||
pub const Payload = struct {
|
||||
tag: Tag,
|
||||
|
@ -1162,12 +1329,38 @@ pub const Value = extern union {
|
|||
ty: Type,
|
||||
};
|
||||
|
||||
pub const IntType = struct {
|
||||
base: Payload = Payload{ .tag = .int_type },
|
||||
bits: u16,
|
||||
signed: bool,
|
||||
};
|
||||
|
||||
pub const Repeated = struct {
|
||||
base: Payload = Payload{ .tag = .ty },
|
||||
/// This value is repeated some number of times. The amount of times to repeat
|
||||
/// is stored externally.
|
||||
val: Value,
|
||||
};
|
||||
|
||||
pub const Float_16 = struct {
|
||||
base: Payload = .{ .tag = .float_16 },
|
||||
val: f16,
|
||||
};
|
||||
|
||||
pub const Float_32 = struct {
|
||||
base: Payload = .{ .tag = .float_32 },
|
||||
val: f32,
|
||||
};
|
||||
|
||||
pub const Float_64 = struct {
|
||||
base: Payload = .{ .tag = .float_64 },
|
||||
val: f64,
|
||||
};
|
||||
|
||||
pub const Float_128 = struct {
|
||||
base: Payload = .{ .tag = .float_128 },
|
||||
val: f128,
|
||||
};
|
||||
};
|
||||
|
||||
/// Big enough to fit any non-BigInt value
|
||||
|
|
|
@ -76,6 +76,7 @@ pub const Inst = struct {
|
|||
primitive,
|
||||
intcast,
|
||||
bitcast,
|
||||
floatcast,
|
||||
elemptr,
|
||||
add,
|
||||
sub,
|
||||
|
@ -137,6 +138,7 @@ pub const Inst = struct {
|
|||
.fntype => FnType,
|
||||
.intcast => IntCast,
|
||||
.bitcast => BitCast,
|
||||
.floatcast => FloatCast,
|
||||
.elemptr => ElemPtr,
|
||||
.condbr => CondBr,
|
||||
};
|
||||
|
@ -169,6 +171,7 @@ pub const Inst = struct {
|
|||
.primitive,
|
||||
.intcast,
|
||||
.bitcast,
|
||||
.floatcast,
|
||||
.elemptr,
|
||||
.add,
|
||||
.sub,
|
||||
|
@ -556,19 +559,33 @@ pub const Inst = struct {
|
|||
};
|
||||
};
|
||||
|
||||
pub const IntCast = struct {
|
||||
pub const base_tag = Tag.intcast;
|
||||
pub const FloatCast = struct {
|
||||
pub const base_tag = Tag.floatcast;
|
||||
pub const builtin_name = "@floatCast";
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
dest_type: *Inst,
|
||||
value: *Inst,
|
||||
operand: *Inst,
|
||||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const IntCast = struct {
|
||||
pub const base_tag = Tag.intcast;
|
||||
pub const builtin_name = "@intCast";
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
dest_type: *Inst,
|
||||
operand: *Inst,
|
||||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const BitCast = struct {
|
||||
pub const base_tag = Tag.bitcast;
|
||||
pub const builtin_name = "@bitCast";
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
|
@ -1618,6 +1635,28 @@ const EmitZIR = struct {
|
|||
return &new_inst.base;
|
||||
}
|
||||
|
||||
fn emitCast(
|
||||
self: *EmitZIR,
|
||||
src: usize,
|
||||
new_body: ZirBody,
|
||||
old_inst: *ir.Inst.UnOp,
|
||||
comptime I: type,
|
||||
) Allocator.Error!*Inst {
|
||||
const new_inst = try self.arena.allocator.create(I);
|
||||
new_inst.* = .{
|
||||
.base = .{
|
||||
.src = src,
|
||||
.tag = I.base_tag,
|
||||
},
|
||||
.positionals = .{
|
||||
.dest_type = (try self.emitType(src, old_inst.base.ty)).inst,
|
||||
.operand = try self.resolveInst(new_body, old_inst.operand),
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
return &new_inst.base;
|
||||
}
|
||||
|
||||
fn emitBody(
|
||||
self: *EmitZIR,
|
||||
body: ir.Body,
|
||||
|
@ -1652,22 +1691,9 @@ const EmitZIR = struct {
|
|||
.cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt),
|
||||
.cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq),
|
||||
|
||||
.bitcast => blk: {
|
||||
const old_inst = inst.castTag(.bitcast).?;
|
||||
const new_inst = try self.arena.allocator.create(Inst.BitCast);
|
||||
new_inst.* = .{
|
||||
.base = .{
|
||||
.src = inst.src,
|
||||
.tag = Inst.BitCast.base_tag,
|
||||
},
|
||||
.positionals = .{
|
||||
.dest_type = (try self.emitType(inst.src, inst.ty)).inst,
|
||||
.operand = try self.resolveInst(new_body, old_inst.operand),
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
.bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, Inst.BitCast),
|
||||
.intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, Inst.IntCast),
|
||||
.floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, Inst.FloatCast),
|
||||
|
||||
.block => blk: {
|
||||
const old_inst = inst.castTag(.block).?;
|
||||
|
|
|
@ -288,6 +288,7 @@ static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* so
|
|||
static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right);
|
||||
static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right);
|
||||
static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field);
|
||||
static void value_to_bigfloat(BigFloat *out, ZigValue *val);
|
||||
|
||||
#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
|
||||
#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) {
|
|||
}
|
||||
BigFloat op1_big;
|
||||
BigFloat op2_big;
|
||||
float_init_bigfloat(op1, &op1_big);
|
||||
float_init_bigfloat(op2, &op2_big);
|
||||
value_to_bigfloat(&op1_big, op1);
|
||||
value_to_bigfloat(&op2_big, op2);
|
||||
return bigfloat_cmp(&op1_big, &op2_big);
|
||||
}
|
||||
|
||||
|
|
|
@ -434,6 +434,17 @@ fn testFloatComparisons() void {
|
|||
}
|
||||
}
|
||||
|
||||
test "different sized float comparisons" {
|
||||
testDifferentSizedFloatComparisons();
|
||||
comptime testDifferentSizedFloatComparisons();
|
||||
}
|
||||
|
||||
fn testDifferentSizedFloatComparisons() void {
|
||||
var a: f16 = 1;
|
||||
var b: f64 = 2;
|
||||
expect(a < b);
|
||||
}
|
||||
|
||||
// TODO This is waiting on library support for the Windows build (not sure why the other's don't need it)
|
||||
//test "@nearbyint" {
|
||||
// comptime testNearbyInt();
|
||||
|
|
Loading…
Reference in New Issue