mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
stage2: implement @truncate
This commit is contained in:
parent
6ae0825e7f
commit
ddf14323ea
11
src/Air.zig
11
src/Air.zig
@ -206,10 +206,16 @@ pub const Inst = struct {
|
||||
/// Convert from one float type to another.
|
||||
/// Uses the `ty_op` field.
|
||||
floatcast,
|
||||
/// TODO audit uses of this. We should have explicit instructions for integer
|
||||
/// widening and truncating.
|
||||
/// Returns an integer with a different type than the operand. The new type may have
|
||||
/// fewer, the same, or more bits than the operand type. However, the instruction
|
||||
/// guarantees that the same integer value fits in both types.
|
||||
/// See `trunc` for integer truncation.
|
||||
/// Uses the `ty_op` field.
|
||||
intcast,
|
||||
/// Truncate higher bits from an integer, resulting in an integer with the same
|
||||
/// sign but an equal or smaller number of bits.
|
||||
/// Uses the `ty_op` field.
|
||||
trunc,
|
||||
/// ?T => T. If the value is null, undefined behavior.
|
||||
/// Uses the `ty_op` field.
|
||||
optional_payload,
|
||||
@ -452,6 +458,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.load,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
.trunc,
|
||||
.optional_payload,
|
||||
.optional_payload_ptr,
|
||||
.wrap_optional,
|
||||
|
||||
@ -264,6 +264,7 @@ fn analyzeInst(
|
||||
.load,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
.trunc,
|
||||
.optional_payload,
|
||||
.optional_payload_ptr,
|
||||
.wrap_optional,
|
||||
|
||||
238
src/Module.zig
238
src/Module.zig
@ -4033,244 +4033,6 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) Co
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
|
||||
pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
|
||||
// TODO is this a performance issue? maybe we should try the operation without
|
||||
// resorting to BigInt first.
|
||||
var lhs_space: Value.BigIntSpace = undefined;
|
||||
var rhs_space: Value.BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result_bigint.add(lhs_bigint, rhs_bigint);
|
||||
const result_limbs = result_bigint.limbs[0..result_bigint.len];
|
||||
|
||||
if (result_bigint.positive) {
|
||||
return Value.Tag.int_big_positive.create(allocator, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(allocator, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
|
||||
// TODO is this a performance issue? maybe we should try the operation without
|
||||
// resorting to BigInt first.
|
||||
var lhs_space: Value.BigIntSpace = undefined;
|
||||
var rhs_space: Value.BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result_bigint.sub(lhs_bigint, rhs_bigint);
|
||||
const result_limbs = result_bigint.limbs[0..result_bigint.len];
|
||||
|
||||
if (result_bigint.positive) {
|
||||
return Value.Tag.int_big_positive.create(allocator, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(allocator, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intDiv(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
|
||||
// TODO is this a performance issue? maybe we should try the operation without
|
||||
// resorting to BigInt first.
|
||||
var lhs_space: Value.BigIntSpace = undefined;
|
||||
var rhs_space: Value.BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs_q = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
|
||||
);
|
||||
const limbs_r = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
lhs_bigint.limbs.len,
|
||||
);
|
||||
const limbs_buffer = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
|
||||
);
|
||||
var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
|
||||
var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
|
||||
result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
|
||||
const result_limbs = result_q.limbs[0..result_q.len];
|
||||
|
||||
if (result_q.positive) {
|
||||
return Value.Tag.int_big_positive.create(allocator, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(allocator, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intMul(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
|
||||
// TODO is this a performance issue? maybe we should try the operation without
|
||||
// resorting to BigInt first.
|
||||
var lhs_space: Value.BigIntSpace = undefined;
|
||||
var rhs_space: Value.BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
var limbs_buffer = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
|
||||
);
|
||||
defer allocator.free(limbs_buffer);
|
||||
result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
|
||||
const result_limbs = result_bigint.limbs[0..result_bigint.len];
|
||||
|
||||
if (result_bigint.positive) {
|
||||
return Value.Tag.int_big_positive.create(allocator, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(allocator, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floatAdd(
|
||||
arena: *Allocator,
|
||||
float_type: Type,
|
||||
src: LazySrcLoc,
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
) !Value {
|
||||
_ = src;
|
||||
switch (float_type.tag()) {
|
||||
.f16 => {
|
||||
@panic("TODO add __trunctfhf2 to compiler-rt");
|
||||
//const lhs_val = lhs.toFloat(f16);
|
||||
//const rhs_val = rhs.toFloat(f16);
|
||||
//return Value.Tag.float_16.create(arena, lhs_val + rhs_val);
|
||||
},
|
||||
.f32 => {
|
||||
const lhs_val = lhs.toFloat(f32);
|
||||
const rhs_val = rhs.toFloat(f32);
|
||||
return Value.Tag.float_32.create(arena, lhs_val + rhs_val);
|
||||
},
|
||||
.f64 => {
|
||||
const lhs_val = lhs.toFloat(f64);
|
||||
const rhs_val = rhs.toFloat(f64);
|
||||
return Value.Tag.float_64.create(arena, lhs_val + rhs_val);
|
||||
},
|
||||
.f128, .comptime_float, .c_longdouble => {
|
||||
const lhs_val = lhs.toFloat(f128);
|
||||
const rhs_val = rhs.toFloat(f128);
|
||||
return Value.Tag.float_128.create(arena, lhs_val + rhs_val);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floatSub(
|
||||
arena: *Allocator,
|
||||
float_type: Type,
|
||||
src: LazySrcLoc,
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
) !Value {
|
||||
_ = src;
|
||||
switch (float_type.tag()) {
|
||||
.f16 => {
|
||||
@panic("TODO add __trunctfhf2 to compiler-rt");
|
||||
//const lhs_val = lhs.toFloat(f16);
|
||||
//const rhs_val = rhs.toFloat(f16);
|
||||
//return Value.Tag.float_16.create(arena, lhs_val - rhs_val);
|
||||
},
|
||||
.f32 => {
|
||||
const lhs_val = lhs.toFloat(f32);
|
||||
const rhs_val = rhs.toFloat(f32);
|
||||
return Value.Tag.float_32.create(arena, lhs_val - rhs_val);
|
||||
},
|
||||
.f64 => {
|
||||
const lhs_val = lhs.toFloat(f64);
|
||||
const rhs_val = rhs.toFloat(f64);
|
||||
return Value.Tag.float_64.create(arena, lhs_val - rhs_val);
|
||||
},
|
||||
.f128, .comptime_float, .c_longdouble => {
|
||||
const lhs_val = lhs.toFloat(f128);
|
||||
const rhs_val = rhs.toFloat(f128);
|
||||
return Value.Tag.float_128.create(arena, lhs_val - rhs_val);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floatDiv(
|
||||
arena: *Allocator,
|
||||
float_type: Type,
|
||||
src: LazySrcLoc,
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
) !Value {
|
||||
_ = src;
|
||||
switch (float_type.tag()) {
|
||||
.f16 => {
|
||||
@panic("TODO add __trunctfhf2 to compiler-rt");
|
||||
//const lhs_val = lhs.toFloat(f16);
|
||||
//const rhs_val = rhs.toFloat(f16);
|
||||
//return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
|
||||
},
|
||||
.f32 => {
|
||||
const lhs_val = lhs.toFloat(f32);
|
||||
const rhs_val = rhs.toFloat(f32);
|
||||
return Value.Tag.float_32.create(arena, lhs_val / rhs_val);
|
||||
},
|
||||
.f64 => {
|
||||
const lhs_val = lhs.toFloat(f64);
|
||||
const rhs_val = rhs.toFloat(f64);
|
||||
return Value.Tag.float_64.create(arena, lhs_val / rhs_val);
|
||||
},
|
||||
.f128, .comptime_float, .c_longdouble => {
|
||||
const lhs_val = lhs.toFloat(f128);
|
||||
const rhs_val = rhs.toFloat(f128);
|
||||
return Value.Tag.float_128.create(arena, lhs_val / rhs_val);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floatMul(
|
||||
arena: *Allocator,
|
||||
float_type: Type,
|
||||
src: LazySrcLoc,
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
) !Value {
|
||||
_ = src;
|
||||
switch (float_type.tag()) {
|
||||
.f16 => {
|
||||
@panic("TODO add __trunctfhf2 to compiler-rt");
|
||||
//const lhs_val = lhs.toFloat(f16);
|
||||
//const rhs_val = rhs.toFloat(f16);
|
||||
//return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
|
||||
},
|
||||
.f32 => {
|
||||
const lhs_val = lhs.toFloat(f32);
|
||||
const rhs_val = rhs.toFloat(f32);
|
||||
return Value.Tag.float_32.create(arena, lhs_val * rhs_val);
|
||||
},
|
||||
.f64 => {
|
||||
const lhs_val = lhs.toFloat(f64);
|
||||
const rhs_val = rhs.toFloat(f64);
|
||||
return Value.Tag.float_64.create(arena, lhs_val * rhs_val);
|
||||
},
|
||||
.f128, .comptime_float, .c_longdouble => {
|
||||
const lhs_val = lhs.toFloat(f128);
|
||||
const rhs_val = rhs.toFloat(f128);
|
||||
return Value.Tag.float_128.create(arena, lhs_val * rhs_val);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simplePtrType(
|
||||
arena: *Allocator,
|
||||
elem_ty: Type,
|
||||
|
||||
104
src/Sema.zig
104
src/Sema.zig
@ -3479,27 +3479,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
|
||||
const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
|
||||
const operand = sema.resolveInst(extra.rhs);
|
||||
|
||||
const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
|
||||
.ComptimeInt => true,
|
||||
.Int => false,
|
||||
else => return sema.mod.fail(
|
||||
&block.base,
|
||||
dest_ty_src,
|
||||
"expected integer type, found '{}'",
|
||||
.{dest_type},
|
||||
),
|
||||
};
|
||||
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
switch (operand_ty.zigTypeTag()) {
|
||||
.ComptimeInt, .Int => {},
|
||||
else => return sema.mod.fail(
|
||||
&block.base,
|
||||
operand_src,
|
||||
"expected integer type, found '{}'",
|
||||
.{operand_ty},
|
||||
),
|
||||
}
|
||||
const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_type);
|
||||
_ = try sema.requireIntegerType(block, operand_src, sema.typeOf(operand));
|
||||
|
||||
if (try sema.isComptimeKnown(block, operand_src, operand)) {
|
||||
return sema.coerce(block, dest_type, operand, operand_src);
|
||||
@ -4951,30 +4932,30 @@ fn analyzeArithmetic(
|
||||
const value = switch (zir_tag) {
|
||||
.add => blk: {
|
||||
const val = if (is_int)
|
||||
try Module.intAdd(sema.arena, lhs_val, rhs_val)
|
||||
try lhs_val.intAdd(rhs_val, sema.arena)
|
||||
else
|
||||
try Module.floatAdd(sema.arena, scalar_type, src, lhs_val, rhs_val);
|
||||
try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena);
|
||||
break :blk val;
|
||||
},
|
||||
.sub => blk: {
|
||||
const val = if (is_int)
|
||||
try Module.intSub(sema.arena, lhs_val, rhs_val)
|
||||
try lhs_val.intSub(rhs_val, sema.arena)
|
||||
else
|
||||
try Module.floatSub(sema.arena, scalar_type, src, lhs_val, rhs_val);
|
||||
try lhs_val.floatSub(rhs_val, scalar_type, sema.arena);
|
||||
break :blk val;
|
||||
},
|
||||
.div => blk: {
|
||||
const val = if (is_int)
|
||||
try Module.intDiv(sema.arena, lhs_val, rhs_val)
|
||||
try lhs_val.intDiv(rhs_val, sema.arena)
|
||||
else
|
||||
try Module.floatDiv(sema.arena, scalar_type, src, lhs_val, rhs_val);
|
||||
try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena);
|
||||
break :blk val;
|
||||
},
|
||||
.mul => blk: {
|
||||
const val = if (is_int)
|
||||
try Module.intMul(sema.arena, lhs_val, rhs_val)
|
||||
try lhs_val.intMul(rhs_val, sema.arena)
|
||||
else
|
||||
try Module.floatMul(sema.arena, scalar_type, src, lhs_val, rhs_val);
|
||||
try lhs_val.floatMul(rhs_val, scalar_type, sema.arena);
|
||||
break :blk val;
|
||||
},
|
||||
else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tag)}),
|
||||
@ -6173,7 +6154,62 @@ fn zirPtrCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
|
||||
fn zirTruncate(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
return sema.mod.fail(&block.base, src, "TODO: Sema.zirTruncate", .{});
|
||||
const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
|
||||
const operand = sema.resolveInst(extra.rhs);
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
const mod = sema.mod;
|
||||
const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_ty);
|
||||
const src_is_comptime_int = try sema.requireIntegerType(block, operand_src, operand_ty);
|
||||
|
||||
if (dest_is_comptime_int) {
|
||||
return sema.coerce(block, dest_ty, operand, operand_src);
|
||||
}
|
||||
|
||||
const target = mod.getTarget();
|
||||
const src_info = operand_ty.intInfo(target);
|
||||
const dest_info = dest_ty.intInfo(target);
|
||||
|
||||
if (src_info.bits == 0 or dest_info.bits == 0) {
|
||||
return sema.addConstant(dest_ty, Value.initTag(.zero));
|
||||
}
|
||||
|
||||
if (!src_is_comptime_int) {
|
||||
if (src_info.signedness != dest_info.signedness) {
|
||||
return mod.fail(&block.base, operand_src, "expected {s} integer type, found '{}'", .{
|
||||
@tagName(dest_info.signedness), operand_ty,
|
||||
});
|
||||
}
|
||||
if (src_info.bits > 0 and src_info.bits < dest_info.bits) {
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(
|
||||
&block.base,
|
||||
src,
|
||||
"destination type '{}' has more bits than source type '{}'",
|
||||
.{ dest_ty, operand_ty },
|
||||
);
|
||||
errdefer msg.destroy(mod.gpa);
|
||||
try mod.errNote(&block.base, dest_ty_src, msg, "destination type has {d} bits", .{
|
||||
dest_info.bits,
|
||||
});
|
||||
try mod.errNote(&block.base, operand_src, msg, "source type has {d} bits", .{
|
||||
src_info.bits,
|
||||
});
|
||||
break :msg msg;
|
||||
};
|
||||
return mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
|
||||
if (val.isUndef()) return sema.addConstUndef(dest_ty);
|
||||
return sema.addConstant(dest_ty, try val.intTrunc(sema.arena, dest_info.bits));
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addTyOp(.trunc, dest_ty, operand);
|
||||
}
|
||||
|
||||
fn zirAlignCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
@ -6594,6 +6630,14 @@ fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void
|
||||
try sema.requireFunctionBlock(block, src);
|
||||
}
|
||||
|
||||
fn requireIntegerType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !bool {
|
||||
switch (ty.zigTypeTag()) {
|
||||
.ComptimeInt => return true,
|
||||
.Int => return false,
|
||||
else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}),
|
||||
}
|
||||
}
|
||||
|
||||
fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {
|
||||
if (!ty.isValidVarType(false)) {
|
||||
return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty});
|
||||
|
||||
@ -835,6 +835,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.dbg_stmt => try self.airDbgStmt(inst),
|
||||
.floatcast => try self.airFloatCast(inst),
|
||||
.intcast => try self.airIntCast(inst),
|
||||
.trunc => try self.airTrunc(inst),
|
||||
.bool_to_int => try self.airBoolToInt(inst),
|
||||
.is_non_null => try self.airIsNonNull(inst),
|
||||
.is_non_null_ptr => try self.airIsNonNullPtr(inst),
|
||||
@ -1109,6 +1110,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
if (self.liveness.isUnused(inst))
|
||||
return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
|
||||
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
_ = operand;
|
||||
const result: MCValue = switch (arch) {
|
||||
else => return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch}),
|
||||
};
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||
const operand = try self.resolveInst(un_op);
|
||||
|
||||
@ -900,6 +900,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
|
||||
.call => try airCall(o, inst),
|
||||
.dbg_stmt => try airDbgStmt(o, inst),
|
||||
.intcast => try airIntCast(o, inst),
|
||||
.trunc => try airTrunc(o, inst),
|
||||
.bool_to_int => try airBoolToInt(o, inst),
|
||||
.load => try airLoad(o, inst),
|
||||
.ret => try airRet(o, inst),
|
||||
@ -1038,7 +1039,7 @@ fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
|
||||
return CValue.none;
|
||||
|
||||
const ty_op = o.air.instructions.items(.data)[inst].ty_op;
|
||||
const from = try o.resolveInst(ty_op.operand);
|
||||
const operand = try o.resolveInst(ty_op.operand);
|
||||
|
||||
const writer = o.writer();
|
||||
const inst_ty = o.air.typeOfIndex(inst);
|
||||
@ -1046,11 +1047,21 @@ fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
|
||||
try writer.writeAll(" = (");
|
||||
try o.dg.renderType(writer, inst_ty);
|
||||
try writer.writeAll(")");
|
||||
try o.writeCValue(writer, from);
|
||||
try o.writeCValue(writer, operand);
|
||||
try writer.writeAll(";\n");
|
||||
return local;
|
||||
}
|
||||
|
||||
fn airTrunc(o: *Object, inst: Air.Inst.Index) !CValue {
|
||||
if (o.liveness.isUnused(inst))
|
||||
return CValue.none;
|
||||
|
||||
const ty_op = o.air.instructions.items(.data)[inst].ty_op;
|
||||
const operand = try o.resolveInst(ty_op.operand);
|
||||
_ = operand;
|
||||
return o.dg.fail("TODO: C backend: airTrunc", .{});
|
||||
}
|
||||
|
||||
fn airBoolToInt(o: *Object, inst: Air.Inst.Index) !CValue {
|
||||
if (o.liveness.isUnused(inst))
|
||||
return CValue.none;
|
||||
|
||||
@ -960,6 +960,7 @@ pub const FuncGen = struct {
|
||||
.call => try self.airCall(inst),
|
||||
.cond_br => try self.airCondBr(inst),
|
||||
.intcast => try self.airIntCast(inst),
|
||||
.trunc => try self.airTrunc(inst),
|
||||
.floatcast => try self.airFloatCast(inst),
|
||||
.ptrtoint => try self.airPtrToInt(inst),
|
||||
.load => try self.airLoad(inst),
|
||||
@ -1615,6 +1616,16 @@ pub const FuncGen = struct {
|
||||
return self.builder.buildIntCast2(operand, try self.dg.llvmType(inst_ty), llvm.Bool.fromBool(signed), "");
|
||||
}
|
||||
|
||||
fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));
|
||||
return self.builder.buildTrunc(operand, dest_llvm_ty, "");
|
||||
}
|
||||
|
||||
fn airFloatCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
@ -423,6 +423,14 @@ pub const Builder = opaque {
|
||||
Idx: c_uint,
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
|
||||
pub const buildTrunc = LLVMBuildTrunc;
|
||||
extern fn LLVMBuildTrunc(
|
||||
*const Builder,
|
||||
Val: *const Value,
|
||||
DestTy: *const Type,
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
};
|
||||
|
||||
pub const IntPredicate = enum(c_int) {
|
||||
|
||||
@ -151,6 +151,7 @@ const Writer = struct {
|
||||
.load,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
.trunc,
|
||||
.optional_payload,
|
||||
.optional_payload_ptr,
|
||||
.wrap_optional,
|
||||
|
||||
238
src/value.zig
238
src/value.zig
@ -1407,6 +1407,244 @@ pub const Value = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
|
||||
// TODO is this a performance issue? maybe we should try the operation without
|
||||
// resorting to BigInt first.
|
||||
var lhs_space: Value.BigIntSpace = undefined;
|
||||
var rhs_space: Value.BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result_bigint.add(lhs_bigint, rhs_bigint);
|
||||
const result_limbs = result_bigint.limbs[0..result_bigint.len];
|
||||
|
||||
if (result_bigint.positive) {
|
||||
return Value.Tag.int_big_positive.create(allocator, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(allocator, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intSub(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
|
||||
// TODO is this a performance issue? maybe we should try the operation without
|
||||
// resorting to BigInt first.
|
||||
var lhs_space: Value.BigIntSpace = undefined;
|
||||
var rhs_space: Value.BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result_bigint.sub(lhs_bigint, rhs_bigint);
|
||||
const result_limbs = result_bigint.limbs[0..result_bigint.len];
|
||||
|
||||
if (result_bigint.positive) {
|
||||
return Value.Tag.int_big_positive.create(allocator, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(allocator, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intDiv(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
|
||||
// TODO is this a performance issue? maybe we should try the operation without
|
||||
// resorting to BigInt first.
|
||||
var lhs_space: Value.BigIntSpace = undefined;
|
||||
var rhs_space: Value.BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs_q = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
|
||||
);
|
||||
const limbs_r = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
lhs_bigint.limbs.len,
|
||||
);
|
||||
const limbs_buffer = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
|
||||
);
|
||||
var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
|
||||
var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
|
||||
result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
|
||||
const result_limbs = result_q.limbs[0..result_q.len];
|
||||
|
||||
if (result_q.positive) {
|
||||
return Value.Tag.int_big_positive.create(allocator, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(allocator, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intMul(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
|
||||
// TODO is this a performance issue? maybe we should try the operation without
|
||||
// resorting to BigInt first.
|
||||
var lhs_space: Value.BigIntSpace = undefined;
|
||||
var rhs_space: Value.BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
var limbs_buffer = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
|
||||
);
|
||||
defer allocator.free(limbs_buffer);
|
||||
result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
|
||||
const result_limbs = result_bigint.limbs[0..result_bigint.len];
|
||||
|
||||
if (result_bigint.positive) {
|
||||
return Value.Tag.int_big_positive.create(allocator, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(allocator, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intTrunc(val: Value, arena: *Allocator, bits: u16) !Value {
|
||||
const x = val.toUnsignedInt(); // TODO: implement comptime truncate on big ints
|
||||
if (bits == 64) return val;
|
||||
const mask = (@as(u64, 1) << @intCast(u6, bits)) - 1;
|
||||
const truncated = x & mask;
|
||||
return Tag.int_u64.create(arena, truncated);
|
||||
}
|
||||
|
||||
pub fn floatAdd(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
float_type: Type,
|
||||
arena: *Allocator,
|
||||
) !Value {
|
||||
switch (float_type.tag()) {
|
||||
.f16 => {
|
||||
@panic("TODO add __trunctfhf2 to compiler-rt");
|
||||
//const lhs_val = lhs.toFloat(f16);
|
||||
//const rhs_val = rhs.toFloat(f16);
|
||||
//return Value.Tag.float_16.create(arena, lhs_val + rhs_val);
|
||||
},
|
||||
.f32 => {
|
||||
const lhs_val = lhs.toFloat(f32);
|
||||
const rhs_val = rhs.toFloat(f32);
|
||||
return Value.Tag.float_32.create(arena, lhs_val + rhs_val);
|
||||
},
|
||||
.f64 => {
|
||||
const lhs_val = lhs.toFloat(f64);
|
||||
const rhs_val = rhs.toFloat(f64);
|
||||
return Value.Tag.float_64.create(arena, lhs_val + rhs_val);
|
||||
},
|
||||
.f128, .comptime_float, .c_longdouble => {
|
||||
const lhs_val = lhs.toFloat(f128);
|
||||
const rhs_val = rhs.toFloat(f128);
|
||||
return Value.Tag.float_128.create(arena, lhs_val + rhs_val);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floatSub(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
float_type: Type,
|
||||
arena: *Allocator,
|
||||
) !Value {
|
||||
switch (float_type.tag()) {
|
||||
.f16 => {
|
||||
@panic("TODO add __trunctfhf2 to compiler-rt");
|
||||
//const lhs_val = lhs.toFloat(f16);
|
||||
//const rhs_val = rhs.toFloat(f16);
|
||||
//return Value.Tag.float_16.create(arena, lhs_val - rhs_val);
|
||||
},
|
||||
.f32 => {
|
||||
const lhs_val = lhs.toFloat(f32);
|
||||
const rhs_val = rhs.toFloat(f32);
|
||||
return Value.Tag.float_32.create(arena, lhs_val - rhs_val);
|
||||
},
|
||||
.f64 => {
|
||||
const lhs_val = lhs.toFloat(f64);
|
||||
const rhs_val = rhs.toFloat(f64);
|
||||
return Value.Tag.float_64.create(arena, lhs_val - rhs_val);
|
||||
},
|
||||
.f128, .comptime_float, .c_longdouble => {
|
||||
const lhs_val = lhs.toFloat(f128);
|
||||
const rhs_val = rhs.toFloat(f128);
|
||||
return Value.Tag.float_128.create(arena, lhs_val - rhs_val);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floatDiv(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
float_type: Type,
|
||||
arena: *Allocator,
|
||||
) !Value {
|
||||
switch (float_type.tag()) {
|
||||
.f16 => {
|
||||
@panic("TODO add __trunctfhf2 to compiler-rt");
|
||||
//const lhs_val = lhs.toFloat(f16);
|
||||
//const rhs_val = rhs.toFloat(f16);
|
||||
//return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
|
||||
},
|
||||
.f32 => {
|
||||
const lhs_val = lhs.toFloat(f32);
|
||||
const rhs_val = rhs.toFloat(f32);
|
||||
return Value.Tag.float_32.create(arena, lhs_val / rhs_val);
|
||||
},
|
||||
.f64 => {
|
||||
const lhs_val = lhs.toFloat(f64);
|
||||
const rhs_val = rhs.toFloat(f64);
|
||||
return Value.Tag.float_64.create(arena, lhs_val / rhs_val);
|
||||
},
|
||||
.f128, .comptime_float, .c_longdouble => {
|
||||
const lhs_val = lhs.toFloat(f128);
|
||||
const rhs_val = rhs.toFloat(f128);
|
||||
return Value.Tag.float_128.create(arena, lhs_val / rhs_val);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floatMul(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
float_type: Type,
|
||||
arena: *Allocator,
|
||||
) !Value {
|
||||
switch (float_type.tag()) {
|
||||
.f16 => {
|
||||
@panic("TODO add __trunctfhf2 to compiler-rt");
|
||||
//const lhs_val = lhs.toFloat(f16);
|
||||
//const rhs_val = rhs.toFloat(f16);
|
||||
//return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
|
||||
},
|
||||
.f32 => {
|
||||
const lhs_val = lhs.toFloat(f32);
|
||||
const rhs_val = rhs.toFloat(f32);
|
||||
return Value.Tag.float_32.create(arena, lhs_val * rhs_val);
|
||||
},
|
||||
.f64 => {
|
||||
const lhs_val = lhs.toFloat(f64);
|
||||
const rhs_val = rhs.toFloat(f64);
|
||||
return Value.Tag.float_64.create(arena, lhs_val * rhs_val);
|
||||
},
|
||||
.f128, .comptime_float, .c_longdouble => {
|
||||
const lhs_val = lhs.toFloat(f128);
|
||||
const rhs_val = rhs.toFloat(f128);
|
||||
return Value.Tag.float_128.create(arena, lhs_val * rhs_val);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// This type is not copyable since it may contain pointers to its inner data.
|
||||
pub const Payload = struct {
|
||||
tag: Tag,
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
// normal comment
|
||||
|
||||
/// this is a documentation comment
|
||||
@ -7,3 +10,11 @@ fn emptyFunctionWithComments() void {}
|
||||
test "empty function with comments" {
|
||||
emptyFunctionWithComments();
|
||||
}
|
||||
|
||||
test "truncate" {
|
||||
try expect(testTruncate(0x10fd) == 0xfd);
|
||||
comptime try expect(testTruncate(0x10fd) == 0xfd);
|
||||
}
|
||||
fn testTruncate(x: u32) u8 {
|
||||
return @truncate(u8, x);
|
||||
}
|
||||
|
||||
@ -5,13 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings;
|
||||
const mem = std.mem;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
test "truncate" {
|
||||
try expect(testTruncate(0x10fd) == 0xfd);
|
||||
}
|
||||
fn testTruncate(x: u32) u8 {
|
||||
return @truncate(u8, x);
|
||||
}
|
||||
|
||||
fn first4KeysOfHomeRow() []const u8 {
|
||||
return "aoeu";
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user