stage2: implement comptime @atomicRmw
* introduce float_to_int and int_to_float AIR instructionts and
implement for the LLVM backend and C backend.
* Sema: implement `zirIntToFloat`.
* Sema: implement `@atomicRmw` comptime evaluation
- introduce `storePtrVal` for when one needs to store a Value to a
pointer which is a Value, and assert it happens at comptime.
* Value: introduce new functionality:
- intToFloat
- numberAddWrap
- numberSubWrap
- numberMax
- numberMin
- bitwiseAnd
- bitwiseNand (not implemented yet)
- bitwiseOr
- bitwiseXor
* Sema: hook up `zirBitwise` to the new Value bitwise implementations
* Type: rename `isFloat` to `isRuntimeFloat` because it returns `false`
for `comptime_float`.
This commit is contained in:
@@ -311,6 +311,12 @@ pub const Inst = struct {
|
||||
/// Given a pointer to an array, return a slice.
|
||||
/// Uses the `ty_op` field.
|
||||
array_to_slice,
|
||||
/// Given a float operand, return the integer with the closest mathematical meaning.
|
||||
/// Uses the `ty_op` field.
|
||||
float_to_int,
|
||||
/// Given an integer operand, return the float with the closest mathematical meaning.
|
||||
/// Uses the `ty_op` field.
|
||||
int_to_float,
|
||||
|
||||
/// Uses the `ty_pl` field with payload `Cmpxchg`.
|
||||
cmpxchg_weak,
|
||||
@@ -598,6 +604,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.struct_field_ptr_index_2,
|
||||
.struct_field_ptr_index_3,
|
||||
.array_to_slice,
|
||||
.float_to_int,
|
||||
.int_to_float,
|
||||
=> return air.getRefType(datas[inst].ty_op.ty),
|
||||
|
||||
.loop,
|
||||
|
||||
@@ -293,6 +293,8 @@ fn analyzeInst(
|
||||
.struct_field_ptr_index_2,
|
||||
.struct_field_ptr_index_3,
|
||||
.array_to_slice,
|
||||
.float_to_int,
|
||||
.int_to_float,
|
||||
=> {
|
||||
const o = inst_datas[inst].ty_op;
|
||||
return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
|
||||
|
||||
240
src/Sema.zig
240
src/Sema.zig
@@ -4830,8 +4830,8 @@ fn analyzeSwitch(
|
||||
var arena = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena.deinit();
|
||||
|
||||
const min_int = try operand_ty.minInt(&arena, mod.getTarget());
|
||||
const max_int = try operand_ty.maxInt(&arena, mod.getTarget());
|
||||
const min_int = try operand_ty.minInt(&arena.allocator, mod.getTarget());
|
||||
const max_int = try operand_ty.maxInt(&arena.allocator, mod.getTarget());
|
||||
if (try range_set.spans(min_int, max_int, operand_ty)) {
|
||||
if (special_prong == .@"else") {
|
||||
return mod.fail(
|
||||
@@ -5671,10 +5671,13 @@ fn zirBitwise(
|
||||
|
||||
if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
|
||||
if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
|
||||
if (lhs_val.isUndef() or rhs_val.isUndef()) {
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});
|
||||
const result_val = switch (air_tag) {
|
||||
.bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena),
|
||||
.bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena),
|
||||
.xor => try lhs_val.bitwiseXor(rhs_val, sema.arena),
|
||||
else => unreachable,
|
||||
};
|
||||
return sema.addConstant(scalar_type, result_val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6028,8 +6031,8 @@ fn analyzeArithmetic(
|
||||
}
|
||||
|
||||
if (zir_tag == .mod_rem) {
|
||||
const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isFloat();
|
||||
const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isFloat();
|
||||
const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isRuntimeFloat();
|
||||
const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isRuntimeFloat();
|
||||
if (dirty_lhs or dirty_rhs) {
|
||||
return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });
|
||||
}
|
||||
@@ -7298,13 +7301,30 @@ fn zirFrameSize(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
|
||||
fn zirFloatToInt(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();
|
||||
// TODO don't forget the safety check!
|
||||
return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{});
|
||||
}
|
||||
|
||||
fn zirIntToFloat(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.zirIntToFloat", .{});
|
||||
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const 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 dest_ty = try sema.resolveType(block, ty_src, extra.lhs);
|
||||
const operand = sema.resolveInst(extra.rhs);
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
|
||||
try sema.checkIntType(block, ty_src, dest_ty);
|
||||
try sema.checkFloatType(block, operand_src, operand_ty);
|
||||
|
||||
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
|
||||
const target = sema.mod.getTarget();
|
||||
const result_val = try val.intToFloat(sema.arena, dest_ty, target);
|
||||
return sema.addConstant(dest_ty, result_val);
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, operand_src);
|
||||
return block.addTyOp(.int_to_float, dest_ty, operand);
|
||||
}
|
||||
|
||||
fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
@@ -7542,6 +7562,34 @@ fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
|
||||
return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});
|
||||
}
|
||||
|
||||
fn checkIntType(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
ty_src: LazySrcLoc,
|
||||
ty: Type,
|
||||
) CompileError!void {
|
||||
switch (ty.zigTypeTag()) {
|
||||
.ComptimeInt, .Int => {},
|
||||
else => return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{
|
||||
ty,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn checkFloatType(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
ty_src: LazySrcLoc,
|
||||
ty: Type,
|
||||
) CompileError!void {
|
||||
switch (ty.zigTypeTag()) {
|
||||
.ComptimeFloat, .Float => {},
|
||||
else => return sema.mod.fail(&block.base, ty_src, "expected float type, found '{}'", .{
|
||||
ty,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn checkAtomicOperandType(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
@@ -7815,9 +7863,23 @@ fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
|
||||
|
||||
const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
|
||||
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| {
|
||||
_ = ptr_val;
|
||||
_ = operand_val;
|
||||
return mod.fail(&block.base, src, "TODO implement Sema for @atomicRmw at comptime", .{});
|
||||
const target = sema.mod.getTarget();
|
||||
const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src;
|
||||
const new_val = switch (op) {
|
||||
// zig fmt: off
|
||||
.Xchg => operand_val,
|
||||
.Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target),
|
||||
.Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target),
|
||||
.And => try stored_val.bitwiseAnd (operand_val, sema.arena),
|
||||
.Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena),
|
||||
.Or => try stored_val.bitwiseOr (operand_val, sema.arena),
|
||||
.Xor => try stored_val.bitwiseXor (operand_val, sema.arena),
|
||||
.Max => try stored_val.numberMax (operand_val, sema.arena),
|
||||
.Min => try stored_val.numberMin (operand_val, sema.arena),
|
||||
// zig fmt: on
|
||||
};
|
||||
try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty);
|
||||
return sema.addConstant(operand_ty, stored_val);
|
||||
} else break :rs operand_src;
|
||||
} else ptr_src;
|
||||
|
||||
@@ -9298,33 +9360,38 @@ fn coerceNum(
|
||||
|
||||
const target = sema.mod.getTarget();
|
||||
|
||||
if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
|
||||
if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
|
||||
if (val.floatHasFraction()) {
|
||||
return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty });
|
||||
switch (dst_zig_tag) {
|
||||
.ComptimeInt, .Int => {
|
||||
if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
|
||||
if (val.floatHasFraction()) {
|
||||
return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty });
|
||||
}
|
||||
return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});
|
||||
} else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
|
||||
if (!val.intFitsInType(dest_type, target)) {
|
||||
return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });
|
||||
}
|
||||
return try sema.addConstant(dest_type, val);
|
||||
}
|
||||
return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});
|
||||
} else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
|
||||
if (!val.intFitsInType(dest_type, target)) {
|
||||
return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });
|
||||
},
|
||||
.ComptimeFloat, .Float => {
|
||||
if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
|
||||
const res = val.floatCast(sema.arena, dest_type) catch |err| switch (err) {
|
||||
error.Overflow => return sema.mod.fail(
|
||||
&block.base,
|
||||
inst_src,
|
||||
"cast of value {} to type '{}' loses information",
|
||||
.{ val, dest_type },
|
||||
),
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
return try sema.addConstant(dest_type, res);
|
||||
} else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
|
||||
const result_val = try val.intToFloat(sema.arena, dest_type, target);
|
||||
return try sema.addConstant(dest_type, result_val);
|
||||
}
|
||||
return try sema.addConstant(dest_type, 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(sema.arena, dest_type) catch |err| switch (err) {
|
||||
error.Overflow => return sema.mod.fail(
|
||||
&block.base,
|
||||
inst_src,
|
||||
"cast of value {} to type '{}' loses information",
|
||||
.{ val, dest_type },
|
||||
),
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
return try sema.addConstant(dest_type, res);
|
||||
} else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
|
||||
return sema.mod.fail(&block.base, inst_src, "TODO int to float", .{});
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -9375,42 +9442,10 @@ fn storePtr2(
|
||||
return;
|
||||
|
||||
const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
|
||||
if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| {
|
||||
const const_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse
|
||||
return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
|
||||
|
||||
if (decl_ref_mut.data.runtime_index < block.runtime_index) {
|
||||
if (block.runtime_cond) |cond_src| {
|
||||
const msg = msg: {
|
||||
const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
if (block.runtime_loop) |loop_src| {
|
||||
const msg = msg: {
|
||||
const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
var new_arena = std.heap.ArenaAllocator.init(sema.gpa);
|
||||
errdefer new_arena.deinit();
|
||||
const new_ty = try elem_ty.copy(&new_arena.allocator);
|
||||
const new_val = try const_val.copy(&new_arena.allocator);
|
||||
const decl = decl_ref_mut.data.decl;
|
||||
var old_arena = decl.value_arena.?.promote(sema.gpa);
|
||||
decl.value_arena = null;
|
||||
try decl.finalizeNewArena(&new_arena);
|
||||
decl.ty = new_ty;
|
||||
decl.val = new_val;
|
||||
old_arena.deinit();
|
||||
const operand_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse
|
||||
return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
|
||||
if (ptr_val.tag() == .decl_ref_mut) {
|
||||
try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
|
||||
return;
|
||||
}
|
||||
break :rs operand_src;
|
||||
@@ -9422,6 +9457,53 @@ fn storePtr2(
|
||||
_ = try block.addBinOp(air_tag, ptr, operand);
|
||||
}
|
||||
|
||||
/// Call when you have Value objects rather than Air instructions, and you want to
|
||||
/// assert the store must be done at comptime.
|
||||
fn storePtrVal(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
ptr_val: Value,
|
||||
operand_val: Value,
|
||||
operand_ty: Type,
|
||||
) !void {
|
||||
if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| {
|
||||
if (decl_ref_mut.data.runtime_index < block.runtime_index) {
|
||||
if (block.runtime_cond) |cond_src| {
|
||||
const msg = msg: {
|
||||
const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
if (block.runtime_loop) |loop_src| {
|
||||
const msg = msg: {
|
||||
const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
var new_arena = std.heap.ArenaAllocator.init(sema.gpa);
|
||||
errdefer new_arena.deinit();
|
||||
const new_ty = try operand_ty.copy(&new_arena.allocator);
|
||||
const new_val = try operand_val.copy(&new_arena.allocator);
|
||||
const decl = decl_ref_mut.data.decl;
|
||||
var old_arena = decl.value_arena.?.promote(sema.gpa);
|
||||
decl.value_arena = null;
|
||||
try decl.finalizeNewArena(&new_arena);
|
||||
decl.ty = new_ty;
|
||||
decl.val = new_val;
|
||||
old_arena.deinit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn bitcast(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
@@ -9801,11 +9883,11 @@ fn cmpNumeric(
|
||||
const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
|
||||
lhs_val.compareWithZero(.lt)
|
||||
else
|
||||
(lhs_ty.isFloat() or lhs_ty.isSignedInt());
|
||||
(lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt());
|
||||
const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
|
||||
rhs_val.compareWithZero(.lt)
|
||||
else
|
||||
(rhs_ty.isFloat() or rhs_ty.isSignedInt());
|
||||
(rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt());
|
||||
const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
|
||||
|
||||
var dest_float_type: ?Type = null;
|
||||
@@ -10031,7 +10113,7 @@ fn resolvePeerTypes(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (chosen_ty.isFloat() and candidate_ty.isFloat()) {
|
||||
if (chosen_ty.isRuntimeFloat() and candidate_ty.isRuntimeFloat()) {
|
||||
if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
|
||||
chosen = candidate;
|
||||
chosen_i = candidate_i + 1;
|
||||
@@ -10049,13 +10131,13 @@ fn resolvePeerTypes(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isFloat()) {
|
||||
if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isRuntimeFloat()) {
|
||||
chosen = candidate;
|
||||
chosen_i = candidate_i + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chosen_ty.isFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {
|
||||
if (chosen_ty.isRuntimeFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -858,6 +858,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.struct_field_ptr=> try self.airStructFieldPtr(inst),
|
||||
.struct_field_val=> try self.airStructFieldVal(inst),
|
||||
.array_to_slice => try self.airArrayToSlice(inst),
|
||||
.int_to_float => try self.airIntToFloat(inst),
|
||||
.float_to_int => try self.airFloatToInt(inst),
|
||||
.cmpxchg_strong => try self.airCmpxchg(inst),
|
||||
.cmpxchg_weak => try self.airCmpxchg(inst),
|
||||
.atomic_rmw => try self.airAtomicRmw(inst),
|
||||
@@ -4769,6 +4771,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
|
||||
else => return self.fail("TODO implement airIntToFloat for {}", .{
|
||||
self.target.cpu.arch,
|
||||
}),
|
||||
};
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
|
||||
else => return self.fail("TODO implement airFloatToInt for {}", .{
|
||||
self.target.cpu.arch,
|
||||
}),
|
||||
};
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.Block, ty_pl.payload);
|
||||
|
||||
@@ -917,6 +917,8 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
|
||||
.atomic_rmw => try airAtomicRmw(o, inst),
|
||||
.atomic_load => try airAtomicLoad(o, inst),
|
||||
|
||||
.int_to_float, .float_to_int => try airSimpleCast(o, inst),
|
||||
|
||||
.atomic_store_unordered => try airAtomicStore(o, inst, toMemoryOrder(.Unordered)),
|
||||
.atomic_store_monotonic => try airAtomicStore(o, inst, toMemoryOrder(.Monotonic)),
|
||||
.atomic_store_release => try airAtomicStore(o, inst, toMemoryOrder(.Release)),
|
||||
@@ -1899,6 +1901,24 @@ fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue {
|
||||
return local;
|
||||
}
|
||||
|
||||
/// Emits a local variable with the result type and initializes it
|
||||
/// with the operand.
|
||||
fn airSimpleCast(o: *Object, inst: Air.Inst.Index) !CValue {
|
||||
if (o.liveness.isUnused(inst))
|
||||
return CValue.none;
|
||||
|
||||
const inst_ty = o.air.typeOfIndex(inst);
|
||||
const local = try o.allocLocal(inst_ty, .Const);
|
||||
const ty_op = o.air.instructions.items(.data)[inst].ty_op;
|
||||
const writer = o.writer();
|
||||
const operand = try o.resolveInst(ty_op.operand);
|
||||
|
||||
try writer.writeAll(" = ");
|
||||
try o.writeCValue(writer, operand);
|
||||
try writer.writeAll(";\n");
|
||||
return local;
|
||||
}
|
||||
|
||||
fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
|
||||
const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = o.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
|
||||
|
||||
@@ -1122,6 +1122,8 @@ pub const FuncGen = struct {
|
||||
.slice_ptr => try self.airSliceField(inst, 0),
|
||||
.slice_len => try self.airSliceField(inst, 1),
|
||||
.array_to_slice => try self.airArrayToSlice(inst),
|
||||
.float_to_int => try self.airFloatToInt(inst),
|
||||
.int_to_float => try self.airIntToFloat(inst),
|
||||
.cmpxchg_weak => try self.airCmpxchg(inst, true),
|
||||
.cmpxchg_strong => try self.airCmpxchg(inst, false),
|
||||
.fence => try self.airFence(inst),
|
||||
@@ -1372,6 +1374,40 @@ pub const FuncGen = struct {
|
||||
return self.builder.buildInsertValue(partial, len, 1, "");
|
||||
}
|
||||
|
||||
fn airIntToFloat(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_ty = self.air.typeOfIndex(inst);
|
||||
const dest_llvm_ty = try self.dg.llvmType(dest_ty);
|
||||
|
||||
if (dest_ty.isSignedInt()) {
|
||||
return self.builder.buildSIToFP(operand, dest_llvm_ty, "");
|
||||
} else {
|
||||
return self.builder.buildUIToFP(operand, dest_llvm_ty, "");
|
||||
}
|
||||
}
|
||||
|
||||
fn airFloatToInt(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_ty = self.air.typeOfIndex(inst);
|
||||
const dest_llvm_ty = try self.dg.llvmType(dest_ty);
|
||||
|
||||
// TODO set fast math flag
|
||||
|
||||
if (dest_ty.isSignedInt()) {
|
||||
return self.builder.buildFPToSI(operand, dest_llvm_ty, "");
|
||||
} else {
|
||||
return self.builder.buildFPToUI(operand, dest_llvm_ty, "");
|
||||
}
|
||||
}
|
||||
|
||||
fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
@@ -1818,7 +1854,7 @@ pub const FuncGen = struct {
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const inst_ty = self.air.typeOfIndex(inst);
|
||||
|
||||
if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, "");
|
||||
if (inst_ty.isRuntimeFloat()) return self.builder.buildFAdd(lhs, rhs, "");
|
||||
if (wrap) return self.builder.buildAdd(lhs, rhs, "");
|
||||
if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
|
||||
return self.builder.buildNUWAdd(lhs, rhs, "");
|
||||
@@ -1833,7 +1869,7 @@ pub const FuncGen = struct {
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const inst_ty = self.air.typeOfIndex(inst);
|
||||
|
||||
if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, "");
|
||||
if (inst_ty.isRuntimeFloat()) return self.builder.buildFSub(lhs, rhs, "");
|
||||
if (wrap) return self.builder.buildSub(lhs, rhs, "");
|
||||
if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
|
||||
return self.builder.buildNUWSub(lhs, rhs, "");
|
||||
@@ -1848,7 +1884,7 @@ pub const FuncGen = struct {
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const inst_ty = self.air.typeOfIndex(inst);
|
||||
|
||||
if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, "");
|
||||
if (inst_ty.isRuntimeFloat()) return self.builder.buildFMul(lhs, rhs, "");
|
||||
if (wrap) return self.builder.buildMul(lhs, rhs, "");
|
||||
if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
|
||||
return self.builder.buildNUWMul(lhs, rhs, "");
|
||||
@@ -1863,7 +1899,7 @@ pub const FuncGen = struct {
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const inst_ty = self.air.typeOfIndex(inst);
|
||||
|
||||
if (inst_ty.isFloat()) return self.builder.buildFDiv(lhs, rhs, "");
|
||||
if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, "");
|
||||
if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
|
||||
return self.builder.buildUDiv(lhs, rhs, "");
|
||||
}
|
||||
@@ -1876,7 +1912,7 @@ pub const FuncGen = struct {
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const inst_ty = self.air.typeOfIndex(inst);
|
||||
|
||||
if (inst_ty.isFloat()) return self.builder.buildFRem(lhs, rhs, "");
|
||||
if (inst_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, "");
|
||||
if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");
|
||||
return self.builder.buildURem(lhs, rhs, "");
|
||||
}
|
||||
@@ -2165,7 +2201,7 @@ pub const FuncGen = struct {
|
||||
const operand_ty = ptr_ty.elemType();
|
||||
const operand = try self.resolveInst(extra.operand);
|
||||
const is_signed_int = operand_ty.isSignedInt();
|
||||
const is_float = operand_ty.isFloat();
|
||||
const is_float = operand_ty.isRuntimeFloat();
|
||||
const op = toLlvmAtomicRmwBinOp(extra.op(), is_signed_int, is_float);
|
||||
const ordering = toLlvmAtomicOrdering(extra.ordering());
|
||||
const single_threaded = llvm.Bool.fromBool(self.single_threaded);
|
||||
|
||||
@@ -563,6 +563,38 @@ pub const Builder = opaque {
|
||||
ordering: AtomicOrdering,
|
||||
singleThread: Bool,
|
||||
) *const Value;
|
||||
|
||||
pub const buildFPToUI = LLVMBuildFPToUI;
|
||||
extern fn LLVMBuildFPToUI(
|
||||
*const Builder,
|
||||
Val: *const Value,
|
||||
DestTy: *const Type,
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
|
||||
pub const buildFPToSI = LLVMBuildFPToSI;
|
||||
extern fn LLVMBuildFPToSI(
|
||||
*const Builder,
|
||||
Val: *const Value,
|
||||
DestTy: *const Type,
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
|
||||
pub const buildUIToFP = LLVMBuildUIToFP;
|
||||
extern fn LLVMBuildUIToFP(
|
||||
*const Builder,
|
||||
Val: *const Value,
|
||||
DestTy: *const Type,
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
|
||||
pub const buildSIToFP = LLVMBuildSIToFP;
|
||||
extern fn LLVMBuildSIToFP(
|
||||
*const Builder,
|
||||
Val: *const Value,
|
||||
DestTy: *const Type,
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
};
|
||||
|
||||
pub const IntPredicate = enum(c_uint) {
|
||||
|
||||
@@ -175,6 +175,8 @@ const Writer = struct {
|
||||
.struct_field_ptr_index_2,
|
||||
.struct_field_ptr_index_3,
|
||||
.array_to_slice,
|
||||
.int_to_float,
|
||||
.float_to_int,
|
||||
=> try w.writeTyOp(s, inst),
|
||||
|
||||
.block,
|
||||
|
||||
45
src/type.zig
45
src/type.zig
@@ -2523,7 +2523,8 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isFloat(self: Type) bool {
|
||||
/// Returns `false` for `comptime_float`.
|
||||
pub fn isRuntimeFloat(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.f16,
|
||||
.f32,
|
||||
@@ -2536,13 +2537,29 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is a fixed-size float.
|
||||
/// Returns `true` for `comptime_float`.
|
||||
pub fn isAnyFloat(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_longdouble,
|
||||
.comptime_float,
|
||||
=> true,
|
||||
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is a fixed-size float or comptime_float.
|
||||
/// Returns 128 for comptime_float types.
|
||||
pub fn floatBits(self: Type, target: Target) u16 {
|
||||
return switch (self.tag()) {
|
||||
.f16 => 16,
|
||||
.f32 => 32,
|
||||
.f64 => 64,
|
||||
.f128 => 128,
|
||||
.f128, .comptime_float => 128,
|
||||
.c_longdouble => CType.longdouble.sizeInBits(target),
|
||||
|
||||
else => unreachable,
|
||||
@@ -2879,7 +2896,7 @@ pub const Type = extern union {
|
||||
}
|
||||
|
||||
/// Asserts that self.zigTypeTag() == .Int.
|
||||
pub fn minInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value {
|
||||
pub fn minInt(self: Type, arena: *Allocator, target: Target) !Value {
|
||||
assert(self.zigTypeTag() == .Int);
|
||||
const info = self.intInfo(target);
|
||||
|
||||
@@ -2889,35 +2906,35 @@ pub const Type = extern union {
|
||||
|
||||
if ((info.bits - 1) <= std.math.maxInt(u6)) {
|
||||
const n: i64 = -(@as(i64, 1) << @truncate(u6, info.bits - 1));
|
||||
return Value.Tag.int_i64.create(&arena.allocator, n);
|
||||
return Value.Tag.int_i64.create(arena, n);
|
||||
}
|
||||
|
||||
var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1);
|
||||
var res = try std.math.big.int.Managed.initSet(arena, 1);
|
||||
try res.shiftLeft(res, info.bits - 1);
|
||||
res.negate();
|
||||
|
||||
const res_const = res.toConst();
|
||||
if (res_const.positive) {
|
||||
return Value.Tag.int_big_positive.create(&arena.allocator, res_const.limbs);
|
||||
return Value.Tag.int_big_positive.create(arena, res_const.limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(&arena.allocator, res_const.limbs);
|
||||
return Value.Tag.int_big_negative.create(arena, res_const.limbs);
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that self.zigTypeTag() == .Int.
|
||||
pub fn maxInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value {
|
||||
pub fn maxInt(self: Type, arena: *Allocator, target: Target) !Value {
|
||||
assert(self.zigTypeTag() == .Int);
|
||||
const info = self.intInfo(target);
|
||||
|
||||
if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) {
|
||||
const n: i64 = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1;
|
||||
return Value.Tag.int_i64.create(&arena.allocator, n);
|
||||
return Value.Tag.int_i64.create(arena, n);
|
||||
} else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) {
|
||||
const n: u64 = (@as(u64, 1) << @truncate(u6, info.bits)) - 1;
|
||||
return Value.Tag.int_u64.create(&arena.allocator, n);
|
||||
return Value.Tag.int_u64.create(arena, n);
|
||||
}
|
||||
|
||||
var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1);
|
||||
var res = try std.math.big.int.Managed.initSet(arena, 1);
|
||||
try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed));
|
||||
const one = std.math.big.int.Const{
|
||||
.limbs = &[_]std.math.big.Limb{1},
|
||||
@@ -2927,9 +2944,9 @@ pub const Type = extern union {
|
||||
|
||||
const res_const = res.toConst();
|
||||
if (res_const.positive) {
|
||||
return Value.Tag.int_big_positive.create(&arena.allocator, res_const.limbs);
|
||||
return Value.Tag.int_big_positive.create(arena, res_const.limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(&arena.allocator, res_const.limbs);
|
||||
return Value.Tag.int_big_negative.create(arena, res_const.limbs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
224
src/value.zig
224
src/value.zig
@@ -1524,6 +1524,230 @@ pub const Value = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn intToFloat(val: Value, allocator: *Allocator, dest_ty: Type, target: Target) !Value {
|
||||
switch (val.tag()) {
|
||||
.undef, .zero, .one => return val,
|
||||
.int_u64 => {
|
||||
return intToFloatInner(val.castTag(.int_u64).?.data, allocator, dest_ty, target);
|
||||
},
|
||||
.int_i64 => {
|
||||
return intToFloatInner(val.castTag(.int_i64).?.data, allocator, dest_ty, target);
|
||||
},
|
||||
.int_big_positive, .int_big_negative => @panic("big int to float"),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn intToFloatInner(x: anytype, arena: *Allocator, dest_ty: Type, target: Target) !Value {
|
||||
switch (dest_ty.floatBits(target)) {
|
||||
16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)),
|
||||
32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)),
|
||||
64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)),
|
||||
128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// Supports both floats and ints; handles undefined.
|
||||
pub fn numberAddWrap(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
ty: Type,
|
||||
arena: *Allocator,
|
||||
target: Target,
|
||||
) !Value {
|
||||
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
|
||||
|
||||
if (ty.isAnyFloat()) {
|
||||
return floatAdd(lhs, rhs, ty, arena);
|
||||
}
|
||||
const result = try intAdd(lhs, rhs, arena);
|
||||
|
||||
const max = try ty.maxInt(arena, target);
|
||||
if (compare(result, .gt, max, ty)) {
|
||||
@panic("TODO comptime wrapping integer addition");
|
||||
}
|
||||
|
||||
const min = try ty.minInt(arena, target);
|
||||
if (compare(result, .lt, min, ty)) {
|
||||
@panic("TODO comptime wrapping integer addition");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Supports both floats and ints; handles undefined.
|
||||
pub fn numberSubWrap(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
ty: Type,
|
||||
arena: *Allocator,
|
||||
target: Target,
|
||||
) !Value {
|
||||
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
|
||||
|
||||
if (ty.isAnyFloat()) {
|
||||
return floatSub(lhs, rhs, ty, arena);
|
||||
}
|
||||
const result = try intSub(lhs, rhs, arena);
|
||||
|
||||
const max = try ty.maxInt(arena, target);
|
||||
if (compare(result, .gt, max, ty)) {
|
||||
@panic("TODO comptime wrapping integer subtraction");
|
||||
}
|
||||
|
||||
const min = try ty.minInt(arena, target);
|
||||
if (compare(result, .lt, min, ty)) {
|
||||
@panic("TODO comptime wrapping integer subtraction");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Supports both floats and ints; handles undefined.
|
||||
pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value {
|
||||
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
|
||||
|
||||
// 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 arena.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
|
||||
switch (lhs_bigint.order(rhs_bigint)) {
|
||||
.lt => result_bigint.copy(rhs_bigint),
|
||||
.gt, .eq => result_bigint.copy(lhs_bigint),
|
||||
}
|
||||
|
||||
const result_limbs = result_bigint.limbs[0..result_bigint.len];
|
||||
|
||||
if (result_bigint.positive) {
|
||||
return Value.Tag.int_big_positive.create(arena, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(arena, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
/// Supports both floats and ints; handles undefined.
|
||||
pub fn numberMin(lhs: Value, rhs: Value, arena: *Allocator) !Value {
|
||||
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
|
||||
|
||||
// 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 arena.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
|
||||
switch (lhs_bigint.order(rhs_bigint)) {
|
||||
.lt => result_bigint.copy(lhs_bigint),
|
||||
.gt, .eq => result_bigint.copy(rhs_bigint),
|
||||
}
|
||||
|
||||
const result_limbs = result_bigint.limbs[0..result_bigint.len];
|
||||
|
||||
if (result_bigint.positive) {
|
||||
return Value.Tag.int_big_positive.create(arena, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(arena, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
/// operands must be integers; handles undefined.
|
||||
pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: *Allocator) !Value {
|
||||
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
|
||||
|
||||
// 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 arena.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result_bigint.bitAnd(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(arena, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(arena, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
/// operands must be integers; handles undefined.
|
||||
pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: *Allocator) !Value {
|
||||
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
|
||||
|
||||
_ = ty;
|
||||
_ = arena;
|
||||
@panic("TODO comptime bitwise NAND");
|
||||
}
|
||||
|
||||
/// operands must be integers; handles undefined.
|
||||
pub fn bitwiseOr(lhs: Value, rhs: Value, arena: *Allocator) !Value {
|
||||
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
|
||||
|
||||
// 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 arena.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result_bigint.bitOr(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(arena, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(arena, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
/// operands must be integers; handles undefined.
|
||||
pub fn bitwiseXor(lhs: Value, rhs: Value, arena: *Allocator) !Value {
|
||||
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
|
||||
|
||||
// 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 arena.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result_bigint.bitXor(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(arena, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(arena, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
@@ -138,3 +138,32 @@ test "atomic store" {
|
||||
@atomicStore(u32, &x, 12345678, .SeqCst);
|
||||
try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
|
||||
}
|
||||
|
||||
test "atomic store comptime" {
|
||||
comptime try testAtomicStore();
|
||||
try testAtomicStore();
|
||||
}
|
||||
|
||||
fn testAtomicStore() !void {
|
||||
var x: u32 = 0;
|
||||
@atomicStore(u32, &x, 1, .SeqCst);
|
||||
try expect(@atomicLoad(u32, &x, .SeqCst) == 1);
|
||||
@atomicStore(u32, &x, 12345678, .SeqCst);
|
||||
try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
|
||||
}
|
||||
|
||||
test "atomicrmw with floats" {
|
||||
try testAtomicRmwFloat();
|
||||
comptime try testAtomicRmwFloat();
|
||||
}
|
||||
|
||||
fn testAtomicRmwFloat() !void {
|
||||
var x: f32 = 0;
|
||||
try expect(x == 0);
|
||||
_ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst);
|
||||
try expect(x == 1);
|
||||
_ = @atomicRmw(f32, &x, .Add, 5, .SeqCst);
|
||||
try expect(x == 6);
|
||||
_ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst);
|
||||
try expect(x == 4);
|
||||
}
|
||||
|
||||
@@ -3,35 +3,6 @@ const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
test "atomic store comptime" {
|
||||
comptime try testAtomicStore();
|
||||
try testAtomicStore();
|
||||
}
|
||||
|
||||
fn testAtomicStore() !void {
|
||||
var x: u32 = 0;
|
||||
@atomicStore(u32, &x, 1, .SeqCst);
|
||||
try expect(@atomicLoad(u32, &x, .SeqCst) == 1);
|
||||
@atomicStore(u32, &x, 12345678, .SeqCst);
|
||||
try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
|
||||
}
|
||||
|
||||
test "atomicrmw with floats" {
|
||||
try testAtomicRmwFloat();
|
||||
comptime try testAtomicRmwFloat();
|
||||
}
|
||||
|
||||
fn testAtomicRmwFloat() !void {
|
||||
var x: f32 = 0;
|
||||
try expect(x == 0);
|
||||
_ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst);
|
||||
try expect(x == 1);
|
||||
_ = @atomicRmw(f32, &x, .Add, 5, .SeqCst);
|
||||
try expect(x == 6);
|
||||
_ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst);
|
||||
try expect(x == 4);
|
||||
}
|
||||
|
||||
test "atomicrmw with ints" {
|
||||
try testAtomicRmwInt();
|
||||
comptime try testAtomicRmwInt();
|
||||
|
||||
Reference in New Issue
Block a user