diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-09-20 14:13:33 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-09-20 14:24:43 -0700 |
| commit | b9d3527e0ed53c4796ab64b4df7daf0909739807 (patch) | |
| tree | 6cb29ce60e606fc4cfd30dad8b6f88c07a6bf8a7 /src/value.zig | |
| parent | 5dc251747b4ea65b1c8d3f2b5af62ca83c6d1196 (diff) | |
| download | zig-b9d3527e0ed53c4796ab64b4df7daf0909739807.tar.gz zig-b9d3527e0ed53c4796ab64b4df7daf0909739807.zip | |
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`.
Diffstat (limited to 'src/value.zig')
| -rw-r--r-- | src/value.zig | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/src/value.zig b/src/value.zig index 88d0d04086..177359d652 100644 --- a/src/value.zig +++ b/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. |
