authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-20 14:13:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-20 14:24:43-07:00
logb9d3527e0ed53c4796ab64b4df7daf0909739807
tree6cb29ce60e606fc4cfd30dad8b6f88c07a6bf8a7
parent5dc251747b4ea65b1c8d3f2b5af62ca83c6d1196

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`.

12 files changed, 573 insertions(+), 128 deletions(-)

src/Air.zig+8
...@@ -311,6 +311,12 @@ pub const Inst = struct {...@@ -311,6 +311,12 @@ pub const Inst = struct {
311 /// Given a pointer to an array, return a slice.311 /// Given a pointer to an array, return a slice.
312 /// Uses the `ty_op` field.312 /// Uses the `ty_op` field.
313 array_to_slice,313 array_to_slice,
314 /// Given a float operand, return the integer with the closest mathematical meaning.
315 /// Uses the `ty_op` field.
316 float_to_int,
317 /// Given an integer operand, return the float with the closest mathematical meaning.
318 /// Uses the `ty_op` field.
319 int_to_float,
314320
315 /// Uses the `ty_pl` field with payload `Cmpxchg`.321 /// Uses the `ty_pl` field with payload `Cmpxchg`.
316 cmpxchg_weak,322 cmpxchg_weak,
...@@ -598,6 +604,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -598,6 +604,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
598 .struct_field_ptr_index_2,604 .struct_field_ptr_index_2,
599 .struct_field_ptr_index_3,605 .struct_field_ptr_index_3,
600 .array_to_slice,606 .array_to_slice,
607 .float_to_int,
608 .int_to_float,
601 => return air.getRefType(datas[inst].ty_op.ty),609 => return air.getRefType(datas[inst].ty_op.ty),
602610
603 .loop,611 .loop,
src/Liveness.zig+2
...@@ -293,6 +293,8 @@ fn analyzeInst(...@@ -293,6 +293,8 @@ fn analyzeInst(
293 .struct_field_ptr_index_2,293 .struct_field_ptr_index_2,
294 .struct_field_ptr_index_3,294 .struct_field_ptr_index_3,
295 .array_to_slice,295 .array_to_slice,
296 .float_to_int,
297 .int_to_float,
296 => {298 => {
297 const o = inst_datas[inst].ty_op;299 const o = inst_datas[inst].ty_op;
298 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });300 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
src/Sema.zig+161-79
...@@ -4830,8 +4830,8 @@ fn analyzeSwitch(...@@ -4830,8 +4830,8 @@ fn analyzeSwitch(
4830 var arena = std.heap.ArenaAllocator.init(gpa);4830 var arena = std.heap.ArenaAllocator.init(gpa);
4831 defer arena.deinit();4831 defer arena.deinit();
48324832
4833 const min_int = try operand_ty.minInt(&arena, mod.getTarget());4833 const min_int = try operand_ty.minInt(&arena.allocator, mod.getTarget());
4834 const max_int = try operand_ty.maxInt(&arena, mod.getTarget());4834 const max_int = try operand_ty.maxInt(&arena.allocator, mod.getTarget());
4835 if (try range_set.spans(min_int, max_int, operand_ty)) {4835 if (try range_set.spans(min_int, max_int, operand_ty)) {
4836 if (special_prong == .@"else") {4836 if (special_prong == .@"else") {
4837 return mod.fail(4837 return mod.fail(
...@@ -5671,10 +5671,13 @@ fn zirBitwise(...@@ -5671,10 +5671,13 @@ fn zirBitwise(
56715671
5672 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {5672 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
5673 if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {5673 if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
5674 if (lhs_val.isUndef() or rhs_val.isUndef()) {5674 const result_val = switch (air_tag) {
5675 return sema.addConstUndef(resolved_type);5675 .bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena),
5676 }5676 .bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena),
5677 return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});5677 .xor => try lhs_val.bitwiseXor(rhs_val, sema.arena),
5678 else => unreachable,
5679 };
5680 return sema.addConstant(scalar_type, result_val);
5678 }5681 }
5679 }5682 }
56805683
...@@ -6028,8 +6031,8 @@ fn analyzeArithmetic(...@@ -6028,8 +6031,8 @@ fn analyzeArithmetic(
6028 }6031 }
60296032
6030 if (zir_tag == .mod_rem) {6033 if (zir_tag == .mod_rem) {
6031 const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isFloat();6034 const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isRuntimeFloat();
6032 const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isFloat();6035 const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isRuntimeFloat();
6033 if (dirty_lhs or dirty_rhs) {6036 if (dirty_lhs or dirty_rhs) {
6034 return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });6037 return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });
6035 }6038 }
...@@ -7298,13 +7301,30 @@ fn zirFrameSize(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE...@@ -7298,13 +7301,30 @@ fn zirFrameSize(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
7298fn zirFloatToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7301fn zirFloatToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7299 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;7302 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7300 const src = inst_data.src();7303 const src = inst_data.src();
7304 // TODO don't forget the safety check!
7301 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{});7305 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{});
7302}7306}
73037307
7304fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7308fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7305 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;7309 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7306 const src = inst_data.src();7310 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
7307 return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToFloat", .{});7311 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
7312 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
7313 const dest_ty = try sema.resolveType(block, ty_src, extra.lhs);
7314 const operand = sema.resolveInst(extra.rhs);
7315 const operand_ty = sema.typeOf(operand);
7316
7317 try sema.checkIntType(block, ty_src, dest_ty);
7318 try sema.checkFloatType(block, operand_src, operand_ty);
7319
7320 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
7321 const target = sema.mod.getTarget();
7322 const result_val = try val.intToFloat(sema.arena, dest_ty, target);
7323 return sema.addConstant(dest_ty, result_val);
7324 }
7325
7326 try sema.requireRuntimeBlock(block, operand_src);
7327 return block.addTyOp(.int_to_float, dest_ty, operand);
7308}7328}
73097329
7310fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7330fn 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...@@ -7542,6 +7562,34 @@ fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
7542 return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});7562 return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});
7543}7563}
75447564
7565fn checkIntType(
7566 sema: *Sema,
7567 block: *Scope.Block,
7568 ty_src: LazySrcLoc,
7569 ty: Type,
7570) CompileError!void {
7571 switch (ty.zigTypeTag()) {
7572 .ComptimeInt, .Int => {},
7573 else => return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{
7574 ty,
7575 }),
7576 }
7577}
7578
7579fn checkFloatType(
7580 sema: *Sema,
7581 block: *Scope.Block,
7582 ty_src: LazySrcLoc,
7583 ty: Type,
7584) CompileError!void {
7585 switch (ty.zigTypeTag()) {
7586 .ComptimeFloat, .Float => {},
7587 else => return sema.mod.fail(&block.base, ty_src, "expected float type, found '{}'", .{
7588 ty,
7589 }),
7590 }
7591}
7592
7545fn checkAtomicOperandType(7593fn checkAtomicOperandType(
7546 sema: *Sema,7594 sema: *Sema,
7547 block: *Scope.Block,7595 block: *Scope.Block,
...@@ -7815,9 +7863,23 @@ fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE...@@ -7815,9 +7863,23 @@ fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
78157863
7816 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {7864 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
7817 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| {7865 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| {
7818 _ = ptr_val;7866 const target = sema.mod.getTarget();
7819 _ = operand_val;7867 const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src;
7820 return mod.fail(&block.base, src, "TODO implement Sema for @atomicRmw at comptime", .{});7868 const new_val = switch (op) {
7869 // zig fmt: off
7870 .Xchg => operand_val,
7871 .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target),
7872 .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target),
7873 .And => try stored_val.bitwiseAnd (operand_val, sema.arena),
7874 .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena),
7875 .Or => try stored_val.bitwiseOr (operand_val, sema.arena),
7876 .Xor => try stored_val.bitwiseXor (operand_val, sema.arena),
7877 .Max => try stored_val.numberMax (operand_val, sema.arena),
7878 .Min => try stored_val.numberMin (operand_val, sema.arena),
7879 // zig fmt: on
7880 };
7881 try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty);
7882 return sema.addConstant(operand_ty, stored_val);
7821 } else break :rs operand_src;7883 } else break :rs operand_src;
7822 } else ptr_src;7884 } else ptr_src;
78237885
...@@ -9298,33 +9360,38 @@ fn coerceNum(...@@ -9298,33 +9360,38 @@ fn coerceNum(
92989360
9299 const target = sema.mod.getTarget();9361 const target = sema.mod.getTarget();
93009362
9301 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {9363 switch (dst_zig_tag) {
9302 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {9364 .ComptimeInt, .Int => {
9303 if (val.floatHasFraction()) {9365 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
9304 return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty });9366 if (val.floatHasFraction()) {
9367 return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty });
9368 }
9369 return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});
9370 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
9371 if (!val.intFitsInType(dest_type, target)) {
9372 return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });
9373 }
9374 return try sema.addConstant(dest_type, val);
9305 }9375 }
9306 return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});9376 },
9307 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {9377 .ComptimeFloat, .Float => {
9308 if (!val.intFitsInType(dest_type, target)) {9378 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
9309 return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });9379 const res = val.floatCast(sema.arena, dest_type) catch |err| switch (err) {
9380 error.Overflow => return sema.mod.fail(
9381 &block.base,
9382 inst_src,
9383 "cast of value {} to type '{}' loses information",
9384 .{ val, dest_type },
9385 ),
9386 error.OutOfMemory => return error.OutOfMemory,
9387 };
9388 return try sema.addConstant(dest_type, res);
9389 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
9390 const result_val = try val.intToFloat(sema.arena, dest_type, target);
9391 return try sema.addConstant(dest_type, result_val);
9310 }9392 }
9311 return try sema.addConstant(dest_type, val);9393 },
9312 }9394 else => {},
9313 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
9314 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
9315 const res = val.floatCast(sema.arena, dest_type) catch |err| switch (err) {
9316 error.Overflow => return sema.mod.fail(
9317 &block.base,
9318 inst_src,
9319 "cast of value {} to type '{}' loses information",
9320 .{ val, dest_type },
9321 ),
9322 error.OutOfMemory => return error.OutOfMemory,
9323 };
9324 return try sema.addConstant(dest_type, res);
9325 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
9326 return sema.mod.fail(&block.base, inst_src, "TODO int to float", .{});
9327 }
9328 }9395 }
9329 return null;9396 return null;
9330}9397}
...@@ -9375,42 +9442,10 @@ fn storePtr2(...@@ -9375,42 +9442,10 @@ fn storePtr2(
9375 return;9442 return;
93769443
9377 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {9444 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
9378 if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| {9445 const operand_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse
9379 const const_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse9446 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
9380 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});9447 if (ptr_val.tag() == .decl_ref_mut) {
93819448 try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
9382 if (decl_ref_mut.data.runtime_index < block.runtime_index) {
9383 if (block.runtime_cond) |cond_src| {
9384 const msg = msg: {
9385 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
9386 errdefer msg.destroy(sema.gpa);
9387 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
9388 break :msg msg;
9389 };
9390 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
9391 }
9392 if (block.runtime_loop) |loop_src| {
9393 const msg = msg: {
9394 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
9395 errdefer msg.destroy(sema.gpa);
9396 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
9397 break :msg msg;
9398 };
9399 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
9400 }
9401 unreachable;
9402 }
9403 var new_arena = std.heap.ArenaAllocator.init(sema.gpa);
9404 errdefer new_arena.deinit();
9405 const new_ty = try elem_ty.copy(&new_arena.allocator);
9406 const new_val = try const_val.copy(&new_arena.allocator);
9407 const decl = decl_ref_mut.data.decl;
9408 var old_arena = decl.value_arena.?.promote(sema.gpa);
9409 decl.value_arena = null;
9410 try decl.finalizeNewArena(&new_arena);
9411 decl.ty = new_ty;
9412 decl.val = new_val;
9413 old_arena.deinit();
9414 return;9449 return;
9415 }9450 }
9416 break :rs operand_src;9451 break :rs operand_src;
...@@ -9422,6 +9457,53 @@ fn storePtr2(...@@ -9422,6 +9457,53 @@ fn storePtr2(
9422 _ = try block.addBinOp(air_tag, ptr, operand);9457 _ = try block.addBinOp(air_tag, ptr, operand);
9423}9458}
94249459
9460/// Call when you have Value objects rather than Air instructions, and you want to
9461/// assert the store must be done at comptime.
9462fn storePtrVal(
9463 sema: *Sema,
9464 block: *Scope.Block,
9465 src: LazySrcLoc,
9466 ptr_val: Value,
9467 operand_val: Value,
9468 operand_ty: Type,
9469) !void {
9470 if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| {
9471 if (decl_ref_mut.data.runtime_index < block.runtime_index) {
9472 if (block.runtime_cond) |cond_src| {
9473 const msg = msg: {
9474 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
9475 errdefer msg.destroy(sema.gpa);
9476 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
9477 break :msg msg;
9478 };
9479 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
9480 }
9481 if (block.runtime_loop) |loop_src| {
9482 const msg = msg: {
9483 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
9484 errdefer msg.destroy(sema.gpa);
9485 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
9486 break :msg msg;
9487 };
9488 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
9489 }
9490 unreachable;
9491 }
9492 var new_arena = std.heap.ArenaAllocator.init(sema.gpa);
9493 errdefer new_arena.deinit();
9494 const new_ty = try operand_ty.copy(&new_arena.allocator);
9495 const new_val = try operand_val.copy(&new_arena.allocator);
9496 const decl = decl_ref_mut.data.decl;
9497 var old_arena = decl.value_arena.?.promote(sema.gpa);
9498 decl.value_arena = null;
9499 try decl.finalizeNewArena(&new_arena);
9500 decl.ty = new_ty;
9501 decl.val = new_val;
9502 old_arena.deinit();
9503 return;
9504 }
9505}
9506
9425fn bitcast(9507fn bitcast(
9426 sema: *Sema,9508 sema: *Sema,
9427 block: *Scope.Block,9509 block: *Scope.Block,
...@@ -9801,11 +9883,11 @@ fn cmpNumeric(...@@ -9801,11 +9883,11 @@ fn cmpNumeric(
9801 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|9883 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
9802 lhs_val.compareWithZero(.lt)9884 lhs_val.compareWithZero(.lt)
9803 else9885 else
9804 (lhs_ty.isFloat() or lhs_ty.isSignedInt());9886 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt());
9805 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|9887 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
9806 rhs_val.compareWithZero(.lt)9888 rhs_val.compareWithZero(.lt)
9807 else9889 else
9808 (rhs_ty.isFloat() or rhs_ty.isSignedInt());9890 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt());
9809 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;9891 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
98109892
9811 var dest_float_type: ?Type = null;9893 var dest_float_type: ?Type = null;
...@@ -10031,7 +10113,7 @@ fn resolvePeerTypes(...@@ -10031,7 +10113,7 @@ fn resolvePeerTypes(
10031 }10113 }
10032 continue;10114 continue;
10033 }10115 }
10034 if (chosen_ty.isFloat() and candidate_ty.isFloat()) {10116 if (chosen_ty.isRuntimeFloat() and candidate_ty.isRuntimeFloat()) {
10035 if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {10117 if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
10036 chosen = candidate;10118 chosen = candidate;
10037 chosen_i = candidate_i + 1;10119 chosen_i = candidate_i + 1;
...@@ -10049,13 +10131,13 @@ fn resolvePeerTypes(...@@ -10049,13 +10131,13 @@ fn resolvePeerTypes(
10049 continue;10131 continue;
10050 }10132 }
1005110133
10052 if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isFloat()) {10134 if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isRuntimeFloat()) {
10053 chosen = candidate;10135 chosen = candidate;
10054 chosen_i = candidate_i + 1;10136 chosen_i = candidate_i + 1;
10055 continue;10137 continue;
10056 }10138 }
1005710139
10058 if (chosen_ty.isFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {10140 if (chosen_ty.isRuntimeFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {
10059 continue;10141 continue;
10060 }10142 }
1006110143
src/codegen.zig+22
...@@ -858,6 +858,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -858,6 +858,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
858 .struct_field_ptr=> try self.airStructFieldPtr(inst),858 .struct_field_ptr=> try self.airStructFieldPtr(inst),
859 .struct_field_val=> try self.airStructFieldVal(inst),859 .struct_field_val=> try self.airStructFieldVal(inst),
860 .array_to_slice => try self.airArrayToSlice(inst),860 .array_to_slice => try self.airArrayToSlice(inst),
861 .int_to_float => try self.airIntToFloat(inst),
862 .float_to_int => try self.airFloatToInt(inst),
861 .cmpxchg_strong => try self.airCmpxchg(inst),863 .cmpxchg_strong => try self.airCmpxchg(inst),
862 .cmpxchg_weak => try self.airCmpxchg(inst),864 .cmpxchg_weak => try self.airCmpxchg(inst),
863 .atomic_rmw => try self.airAtomicRmw(inst),865 .atomic_rmw => try self.airAtomicRmw(inst),
...@@ -4769,6 +4771,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4769,6 +4771,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4769 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });4771 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4770 }4772 }
47714773
4774 fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {
4775 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4776 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
4777 else => return self.fail("TODO implement airIntToFloat for {}", .{
4778 self.target.cpu.arch,
4779 }),
4780 };
4781 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4782 }
4783
4784 fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
4785 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4786 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
4787 else => return self.fail("TODO implement airFloatToInt for {}", .{
4788 self.target.cpu.arch,
4789 }),
4790 };
4791 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4792 }
4793
4772 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {4794 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
4773 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4795 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4774 const extra = self.air.extraData(Air.Block, ty_pl.payload);4796 const extra = self.air.extraData(Air.Block, ty_pl.payload);
src/codegen/c.zig+20
...@@ -917,6 +917,8 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM...@@ -917,6 +917,8 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
917 .atomic_rmw => try airAtomicRmw(o, inst),917 .atomic_rmw => try airAtomicRmw(o, inst),
918 .atomic_load => try airAtomicLoad(o, inst),918 .atomic_load => try airAtomicLoad(o, inst),
919919
920 .int_to_float, .float_to_int => try airSimpleCast(o, inst),
921
920 .atomic_store_unordered => try airAtomicStore(o, inst, toMemoryOrder(.Unordered)),922 .atomic_store_unordered => try airAtomicStore(o, inst, toMemoryOrder(.Unordered)),
921 .atomic_store_monotonic => try airAtomicStore(o, inst, toMemoryOrder(.Monotonic)),923 .atomic_store_monotonic => try airAtomicStore(o, inst, toMemoryOrder(.Monotonic)),
922 .atomic_store_release => try airAtomicStore(o, inst, toMemoryOrder(.Release)),924 .atomic_store_release => try airAtomicStore(o, inst, toMemoryOrder(.Release)),
...@@ -1899,6 +1901,24 @@ fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue {...@@ -1899,6 +1901,24 @@ fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue {
1899 return local;1901 return local;
1900}1902}
19011903
1904/// Emits a local variable with the result type and initializes it
1905/// with the operand.
1906fn airSimpleCast(o: *Object, inst: Air.Inst.Index) !CValue {
1907 if (o.liveness.isUnused(inst))
1908 return CValue.none;
1909
1910 const inst_ty = o.air.typeOfIndex(inst);
1911 const local = try o.allocLocal(inst_ty, .Const);
1912 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1913 const writer = o.writer();
1914 const operand = try o.resolveInst(ty_op.operand);
1915
1916 try writer.writeAll(" = ");
1917 try o.writeCValue(writer, operand);
1918 try writer.writeAll(";\n");
1919 return local;
1920}
1921
1902fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {1922fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
1903 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;1923 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1904 const extra = o.air.extraData(Air.Cmpxchg, ty_pl.payload).data;1924 const extra = o.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
src/codegen/llvm.zig+42-6
...@@ -1122,6 +1122,8 @@ pub const FuncGen = struct {...@@ -1122,6 +1122,8 @@ pub const FuncGen = struct {
1122 .slice_ptr => try self.airSliceField(inst, 0),1122 .slice_ptr => try self.airSliceField(inst, 0),
1123 .slice_len => try self.airSliceField(inst, 1),1123 .slice_len => try self.airSliceField(inst, 1),
1124 .array_to_slice => try self.airArrayToSlice(inst),1124 .array_to_slice => try self.airArrayToSlice(inst),
1125 .float_to_int => try self.airFloatToInt(inst),
1126 .int_to_float => try self.airIntToFloat(inst),
1125 .cmpxchg_weak => try self.airCmpxchg(inst, true),1127 .cmpxchg_weak => try self.airCmpxchg(inst, true),
1126 .cmpxchg_strong => try self.airCmpxchg(inst, false),1128 .cmpxchg_strong => try self.airCmpxchg(inst, false),
1127 .fence => try self.airFence(inst),1129 .fence => try self.airFence(inst),
...@@ -1372,6 +1374,40 @@ pub const FuncGen = struct {...@@ -1372,6 +1374,40 @@ pub const FuncGen = struct {
1372 return self.builder.buildInsertValue(partial, len, 1, "");1374 return self.builder.buildInsertValue(partial, len, 1, "");
1373 }1375 }
13741376
1377 fn airIntToFloat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1378 if (self.liveness.isUnused(inst))
1379 return null;
1380
1381 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1382 const operand = try self.resolveInst(ty_op.operand);
1383 const dest_ty = self.air.typeOfIndex(inst);
1384 const dest_llvm_ty = try self.dg.llvmType(dest_ty);
1385
1386 if (dest_ty.isSignedInt()) {
1387 return self.builder.buildSIToFP(operand, dest_llvm_ty, "");
1388 } else {
1389 return self.builder.buildUIToFP(operand, dest_llvm_ty, "");
1390 }
1391 }
1392
1393 fn airFloatToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1394 if (self.liveness.isUnused(inst))
1395 return null;
1396
1397 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1398 const operand = try self.resolveInst(ty_op.operand);
1399 const dest_ty = self.air.typeOfIndex(inst);
1400 const dest_llvm_ty = try self.dg.llvmType(dest_ty);
1401
1402 // TODO set fast math flag
1403
1404 if (dest_ty.isSignedInt()) {
1405 return self.builder.buildFPToSI(operand, dest_llvm_ty, "");
1406 } else {
1407 return self.builder.buildFPToUI(operand, dest_llvm_ty, "");
1408 }
1409 }
1410
1375 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {1411 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {
1376 if (self.liveness.isUnused(inst))1412 if (self.liveness.isUnused(inst))
1377 return null;1413 return null;
...@@ -1818,7 +1854,7 @@ pub const FuncGen = struct {...@@ -1818,7 +1854,7 @@ pub const FuncGen = struct {
1818 const rhs = try self.resolveInst(bin_op.rhs);1854 const rhs = try self.resolveInst(bin_op.rhs);
1819 const inst_ty = self.air.typeOfIndex(inst);1855 const inst_ty = self.air.typeOfIndex(inst);
18201856
1821 if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, "");1857 if (inst_ty.isRuntimeFloat()) return self.builder.buildFAdd(lhs, rhs, "");
1822 if (wrap) return self.builder.buildAdd(lhs, rhs, "");1858 if (wrap) return self.builder.buildAdd(lhs, rhs, "");
1823 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");1859 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
1824 return self.builder.buildNUWAdd(lhs, rhs, "");1860 return self.builder.buildNUWAdd(lhs, rhs, "");
...@@ -1833,7 +1869,7 @@ pub const FuncGen = struct {...@@ -1833,7 +1869,7 @@ pub const FuncGen = struct {
1833 const rhs = try self.resolveInst(bin_op.rhs);1869 const rhs = try self.resolveInst(bin_op.rhs);
1834 const inst_ty = self.air.typeOfIndex(inst);1870 const inst_ty = self.air.typeOfIndex(inst);
18351871
1836 if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, "");1872 if (inst_ty.isRuntimeFloat()) return self.builder.buildFSub(lhs, rhs, "");
1837 if (wrap) return self.builder.buildSub(lhs, rhs, "");1873 if (wrap) return self.builder.buildSub(lhs, rhs, "");
1838 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");1874 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
1839 return self.builder.buildNUWSub(lhs, rhs, "");1875 return self.builder.buildNUWSub(lhs, rhs, "");
...@@ -1848,7 +1884,7 @@ pub const FuncGen = struct {...@@ -1848,7 +1884,7 @@ pub const FuncGen = struct {
1848 const rhs = try self.resolveInst(bin_op.rhs);1884 const rhs = try self.resolveInst(bin_op.rhs);
1849 const inst_ty = self.air.typeOfIndex(inst);1885 const inst_ty = self.air.typeOfIndex(inst);
18501886
1851 if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, "");1887 if (inst_ty.isRuntimeFloat()) return self.builder.buildFMul(lhs, rhs, "");
1852 if (wrap) return self.builder.buildMul(lhs, rhs, "");1888 if (wrap) return self.builder.buildMul(lhs, rhs, "");
1853 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");1889 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
1854 return self.builder.buildNUWMul(lhs, rhs, "");1890 return self.builder.buildNUWMul(lhs, rhs, "");
...@@ -1863,7 +1899,7 @@ pub const FuncGen = struct {...@@ -1863,7 +1899,7 @@ pub const FuncGen = struct {
1863 const rhs = try self.resolveInst(bin_op.rhs);1899 const rhs = try self.resolveInst(bin_op.rhs);
1864 const inst_ty = self.air.typeOfIndex(inst);1900 const inst_ty = self.air.typeOfIndex(inst);
18651901
1866 if (inst_ty.isFloat()) return self.builder.buildFDiv(lhs, rhs, "");1902 if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, "");
1867 if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");1903 if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
1868 return self.builder.buildUDiv(lhs, rhs, "");1904 return self.builder.buildUDiv(lhs, rhs, "");
1869 }1905 }
...@@ -1876,7 +1912,7 @@ pub const FuncGen = struct {...@@ -1876,7 +1912,7 @@ pub const FuncGen = struct {
1876 const rhs = try self.resolveInst(bin_op.rhs);1912 const rhs = try self.resolveInst(bin_op.rhs);
1877 const inst_ty = self.air.typeOfIndex(inst);1913 const inst_ty = self.air.typeOfIndex(inst);
18781914
1879 if (inst_ty.isFloat()) return self.builder.buildFRem(lhs, rhs, "");1915 if (inst_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, "");
1880 if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");1916 if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");
1881 return self.builder.buildURem(lhs, rhs, "");1917 return self.builder.buildURem(lhs, rhs, "");
1882 }1918 }
...@@ -2165,7 +2201,7 @@ pub const FuncGen = struct {...@@ -2165,7 +2201,7 @@ pub const FuncGen = struct {
2165 const operand_ty = ptr_ty.elemType();2201 const operand_ty = ptr_ty.elemType();
2166 const operand = try self.resolveInst(extra.operand);2202 const operand = try self.resolveInst(extra.operand);
2167 const is_signed_int = operand_ty.isSignedInt();2203 const is_signed_int = operand_ty.isSignedInt();
2168 const is_float = operand_ty.isFloat();2204 const is_float = operand_ty.isRuntimeFloat();
2169 const op = toLlvmAtomicRmwBinOp(extra.op(), is_signed_int, is_float);2205 const op = toLlvmAtomicRmwBinOp(extra.op(), is_signed_int, is_float);
2170 const ordering = toLlvmAtomicOrdering(extra.ordering());2206 const ordering = toLlvmAtomicOrdering(extra.ordering());
2171 const single_threaded = llvm.Bool.fromBool(self.single_threaded);2207 const single_threaded = llvm.Bool.fromBool(self.single_threaded);
src/codegen/llvm/bindings.zig+32
...@@ -563,6 +563,38 @@ pub const Builder = opaque {...@@ -563,6 +563,38 @@ pub const Builder = opaque {
563 ordering: AtomicOrdering,563 ordering: AtomicOrdering,
564 singleThread: Bool,564 singleThread: Bool,
565 ) *const Value;565 ) *const Value;
566
567 pub const buildFPToUI = LLVMBuildFPToUI;
568 extern fn LLVMBuildFPToUI(
569 *const Builder,
570 Val: *const Value,
571 DestTy: *const Type,
572 Name: [*:0]const u8,
573 ) *const Value;
574
575 pub const buildFPToSI = LLVMBuildFPToSI;
576 extern fn LLVMBuildFPToSI(
577 *const Builder,
578 Val: *const Value,
579 DestTy: *const Type,
580 Name: [*:0]const u8,
581 ) *const Value;
582
583 pub const buildUIToFP = LLVMBuildUIToFP;
584 extern fn LLVMBuildUIToFP(
585 *const Builder,
586 Val: *const Value,
587 DestTy: *const Type,
588 Name: [*:0]const u8,
589 ) *const Value;
590
591 pub const buildSIToFP = LLVMBuildSIToFP;
592 extern fn LLVMBuildSIToFP(
593 *const Builder,
594 Val: *const Value,
595 DestTy: *const Type,
596 Name: [*:0]const u8,
597 ) *const Value;
566};598};
567599
568pub const IntPredicate = enum(c_uint) {600pub const IntPredicate = enum(c_uint) {
src/print_air.zig+2
...@@ -175,6 +175,8 @@ const Writer = struct {...@@ -175,6 +175,8 @@ const Writer = struct {
175 .struct_field_ptr_index_2,175 .struct_field_ptr_index_2,
176 .struct_field_ptr_index_3,176 .struct_field_ptr_index_3,
177 .array_to_slice,177 .array_to_slice,
178 .int_to_float,
179 .float_to_int,
178 => try w.writeTyOp(s, inst),180 => try w.writeTyOp(s, inst),
179181
180 .block,182 .block,
src/type.zig+31-14
...@@ -2523,7 +2523,8 @@ pub const Type = extern union {...@@ -2523,7 +2523,8 @@ pub const Type = extern union {
2523 };2523 };
2524 }2524 }
25252525
2526 pub fn isFloat(self: Type) bool {2526 /// Returns `false` for `comptime_float`.
2527 pub fn isRuntimeFloat(self: Type) bool {
2527 return switch (self.tag()) {2528 return switch (self.tag()) {
2528 .f16,2529 .f16,
2529 .f32,2530 .f32,
...@@ -2536,13 +2537,29 @@ pub const Type = extern union {...@@ -2536,13 +2537,29 @@ pub const Type = extern union {
2536 };2537 };
2537 }2538 }
25382539
2539 /// Asserts the type is a fixed-size float.2540 /// Returns `true` for `comptime_float`.
2541 pub fn isAnyFloat(self: Type) bool {
2542 return switch (self.tag()) {
2543 .f16,
2544 .f32,
2545 .f64,
2546 .f128,
2547 .c_longdouble,
2548 .comptime_float,
2549 => true,
2550
2551 else => false,
2552 };
2553 }
2554
2555 /// Asserts the type is a fixed-size float or comptime_float.
2556 /// Returns 128 for comptime_float types.
2540 pub fn floatBits(self: Type, target: Target) u16 {2557 pub fn floatBits(self: Type, target: Target) u16 {
2541 return switch (self.tag()) {2558 return switch (self.tag()) {
2542 .f16 => 16,2559 .f16 => 16,
2543 .f32 => 32,2560 .f32 => 32,
2544 .f64 => 64,2561 .f64 => 64,
2545 .f128 => 128,2562 .f128, .comptime_float => 128,
2546 .c_longdouble => CType.longdouble.sizeInBits(target),2563 .c_longdouble => CType.longdouble.sizeInBits(target),
25472564
2548 else => unreachable,2565 else => unreachable,
...@@ -2879,7 +2896,7 @@ pub const Type = extern union {...@@ -2879,7 +2896,7 @@ pub const Type = extern union {
2879 }2896 }
28802897
2881 /// Asserts that self.zigTypeTag() == .Int.2898 /// Asserts that self.zigTypeTag() == .Int.
2882 pub fn minInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value {2899 pub fn minInt(self: Type, arena: *Allocator, target: Target) !Value {
2883 assert(self.zigTypeTag() == .Int);2900 assert(self.zigTypeTag() == .Int);
2884 const info = self.intInfo(target);2901 const info = self.intInfo(target);
28852902
...@@ -2889,35 +2906,35 @@ pub const Type = extern union {...@@ -2889,35 +2906,35 @@ pub const Type = extern union {
28892906
2890 if ((info.bits - 1) <= std.math.maxInt(u6)) {2907 if ((info.bits - 1) <= std.math.maxInt(u6)) {
2891 const n: i64 = -(@as(i64, 1) << @truncate(u6, info.bits - 1));2908 const n: i64 = -(@as(i64, 1) << @truncate(u6, info.bits - 1));
2892 return Value.Tag.int_i64.create(&arena.allocator, n);2909 return Value.Tag.int_i64.create(arena, n);
2893 }2910 }
28942911
2895 var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1);2912 var res = try std.math.big.int.Managed.initSet(arena, 1);
2896 try res.shiftLeft(res, info.bits - 1);2913 try res.shiftLeft(res, info.bits - 1);
2897 res.negate();2914 res.negate();
28982915
2899 const res_const = res.toConst();2916 const res_const = res.toConst();
2900 if (res_const.positive) {2917 if (res_const.positive) {
2901 return Value.Tag.int_big_positive.create(&arena.allocator, res_const.limbs);2918 return Value.Tag.int_big_positive.create(arena, res_const.limbs);
2902 } else {2919 } else {
2903 return Value.Tag.int_big_negative.create(&arena.allocator, res_const.limbs);2920 return Value.Tag.int_big_negative.create(arena, res_const.limbs);
2904 }2921 }
2905 }2922 }
29062923
2907 /// Asserts that self.zigTypeTag() == .Int.2924 /// Asserts that self.zigTypeTag() == .Int.
2908 pub fn maxInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value {2925 pub fn maxInt(self: Type, arena: *Allocator, target: Target) !Value {
2909 assert(self.zigTypeTag() == .Int);2926 assert(self.zigTypeTag() == .Int);
2910 const info = self.intInfo(target);2927 const info = self.intInfo(target);
29112928
2912 if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) {2929 if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) {
2913 const n: i64 = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1;2930 const n: i64 = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1;
2914 return Value.Tag.int_i64.create(&arena.allocator, n);2931 return Value.Tag.int_i64.create(arena, n);
2915 } else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) {2932 } else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) {
2916 const n: u64 = (@as(u64, 1) << @truncate(u6, info.bits)) - 1;2933 const n: u64 = (@as(u64, 1) << @truncate(u6, info.bits)) - 1;
2917 return Value.Tag.int_u64.create(&arena.allocator, n);2934 return Value.Tag.int_u64.create(arena, n);
2918 }2935 }
29192936
2920 var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1);2937 var res = try std.math.big.int.Managed.initSet(arena, 1);
2921 try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed));2938 try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed));
2922 const one = std.math.big.int.Const{2939 const one = std.math.big.int.Const{
2923 .limbs = &[_]std.math.big.Limb{1},2940 .limbs = &[_]std.math.big.Limb{1},
...@@ -2927,9 +2944,9 @@ pub const Type = extern union {...@@ -2927,9 +2944,9 @@ pub const Type = extern union {
29272944
2928 const res_const = res.toConst();2945 const res_const = res.toConst();
2929 if (res_const.positive) {2946 if (res_const.positive) {
2930 return Value.Tag.int_big_positive.create(&arena.allocator, res_const.limbs);2947 return Value.Tag.int_big_positive.create(arena, res_const.limbs);
2931 } else {2948 } else {
2932 return Value.Tag.int_big_negative.create(&arena.allocator, res_const.limbs);2949 return Value.Tag.int_big_negative.create(arena, res_const.limbs);
2933 }2950 }
2934 }2951 }
29352952
src/value.zig+224
...@@ -1524,6 +1524,230 @@ pub const Value = extern union {...@@ -1524,6 +1524,230 @@ pub const Value = extern union {
1524 };1524 };
1525 }1525 }
15261526
1527 pub fn intToFloat(val: Value, allocator: *Allocator, dest_ty: Type, target: Target) !Value {
1528 switch (val.tag()) {
1529 .undef, .zero, .one => return val,
1530 .int_u64 => {
1531 return intToFloatInner(val.castTag(.int_u64).?.data, allocator, dest_ty, target);
1532 },
1533 .int_i64 => {
1534 return intToFloatInner(val.castTag(.int_i64).?.data, allocator, dest_ty, target);
1535 },
1536 .int_big_positive, .int_big_negative => @panic("big int to float"),
1537 else => unreachable,
1538 }
1539 }
1540
1541 fn intToFloatInner(x: anytype, arena: *Allocator, dest_ty: Type, target: Target) !Value {
1542 switch (dest_ty.floatBits(target)) {
1543 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)),
1544 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)),
1545 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)),
1546 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)),
1547 else => unreachable,
1548 }
1549 }
1550
1551 /// Supports both floats and ints; handles undefined.
1552 pub fn numberAddWrap(
1553 lhs: Value,
1554 rhs: Value,
1555 ty: Type,
1556 arena: *Allocator,
1557 target: Target,
1558 ) !Value {
1559 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1560
1561 if (ty.isAnyFloat()) {
1562 return floatAdd(lhs, rhs, ty, arena);
1563 }
1564 const result = try intAdd(lhs, rhs, arena);
1565
1566 const max = try ty.maxInt(arena, target);
1567 if (compare(result, .gt, max, ty)) {
1568 @panic("TODO comptime wrapping integer addition");
1569 }
1570
1571 const min = try ty.minInt(arena, target);
1572 if (compare(result, .lt, min, ty)) {
1573 @panic("TODO comptime wrapping integer addition");
1574 }
1575
1576 return result;
1577 }
1578
1579 /// Supports both floats and ints; handles undefined.
1580 pub fn numberSubWrap(
1581 lhs: Value,
1582 rhs: Value,
1583 ty: Type,
1584 arena: *Allocator,
1585 target: Target,
1586 ) !Value {
1587 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1588
1589 if (ty.isAnyFloat()) {
1590 return floatSub(lhs, rhs, ty, arena);
1591 }
1592 const result = try intSub(lhs, rhs, arena);
1593
1594 const max = try ty.maxInt(arena, target);
1595 if (compare(result, .gt, max, ty)) {
1596 @panic("TODO comptime wrapping integer subtraction");
1597 }
1598
1599 const min = try ty.minInt(arena, target);
1600 if (compare(result, .lt, min, ty)) {
1601 @panic("TODO comptime wrapping integer subtraction");
1602 }
1603
1604 return result;
1605 }
1606
1607 /// Supports both floats and ints; handles undefined.
1608 pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1609 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1610
1611 // TODO is this a performance issue? maybe we should try the operation without
1612 // resorting to BigInt first.
1613 var lhs_space: Value.BigIntSpace = undefined;
1614 var rhs_space: Value.BigIntSpace = undefined;
1615 const lhs_bigint = lhs.toBigInt(&lhs_space);
1616 const rhs_bigint = rhs.toBigInt(&rhs_space);
1617 const limbs = try arena.alloc(
1618 std.math.big.Limb,
1619 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1620 );
1621 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1622
1623 switch (lhs_bigint.order(rhs_bigint)) {
1624 .lt => result_bigint.copy(rhs_bigint),
1625 .gt, .eq => result_bigint.copy(lhs_bigint),
1626 }
1627
1628 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1629
1630 if (result_bigint.positive) {
1631 return Value.Tag.int_big_positive.create(arena, result_limbs);
1632 } else {
1633 return Value.Tag.int_big_negative.create(arena, result_limbs);
1634 }
1635 }
1636
1637 /// Supports both floats and ints; handles undefined.
1638 pub fn numberMin(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1639 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1640
1641 // TODO is this a performance issue? maybe we should try the operation without
1642 // resorting to BigInt first.
1643 var lhs_space: Value.BigIntSpace = undefined;
1644 var rhs_space: Value.BigIntSpace = undefined;
1645 const lhs_bigint = lhs.toBigInt(&lhs_space);
1646 const rhs_bigint = rhs.toBigInt(&rhs_space);
1647 const limbs = try arena.alloc(
1648 std.math.big.Limb,
1649 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1650 );
1651 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1652
1653 switch (lhs_bigint.order(rhs_bigint)) {
1654 .lt => result_bigint.copy(lhs_bigint),
1655 .gt, .eq => result_bigint.copy(rhs_bigint),
1656 }
1657
1658 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1659
1660 if (result_bigint.positive) {
1661 return Value.Tag.int_big_positive.create(arena, result_limbs);
1662 } else {
1663 return Value.Tag.int_big_negative.create(arena, result_limbs);
1664 }
1665 }
1666
1667 /// operands must be integers; handles undefined.
1668 pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1669 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1670
1671 // TODO is this a performance issue? maybe we should try the operation without
1672 // resorting to BigInt first.
1673 var lhs_space: Value.BigIntSpace = undefined;
1674 var rhs_space: Value.BigIntSpace = undefined;
1675 const lhs_bigint = lhs.toBigInt(&lhs_space);
1676 const rhs_bigint = rhs.toBigInt(&rhs_space);
1677 const limbs = try arena.alloc(
1678 std.math.big.Limb,
1679 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1680 );
1681 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1682 result_bigint.bitAnd(lhs_bigint, rhs_bigint);
1683 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1684
1685 if (result_bigint.positive) {
1686 return Value.Tag.int_big_positive.create(arena, result_limbs);
1687 } else {
1688 return Value.Tag.int_big_negative.create(arena, result_limbs);
1689 }
1690 }
1691
1692 /// operands must be integers; handles undefined.
1693 pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: *Allocator) !Value {
1694 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1695
1696 _ = ty;
1697 _ = arena;
1698 @panic("TODO comptime bitwise NAND");
1699 }
1700
1701 /// operands must be integers; handles undefined.
1702 pub fn bitwiseOr(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1703 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1704
1705 // TODO is this a performance issue? maybe we should try the operation without
1706 // resorting to BigInt first.
1707 var lhs_space: Value.BigIntSpace = undefined;
1708 var rhs_space: Value.BigIntSpace = undefined;
1709 const lhs_bigint = lhs.toBigInt(&lhs_space);
1710 const rhs_bigint = rhs.toBigInt(&rhs_space);
1711 const limbs = try arena.alloc(
1712 std.math.big.Limb,
1713 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1714 );
1715 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1716 result_bigint.bitOr(lhs_bigint, rhs_bigint);
1717 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1718
1719 if (result_bigint.positive) {
1720 return Value.Tag.int_big_positive.create(arena, result_limbs);
1721 } else {
1722 return Value.Tag.int_big_negative.create(arena, result_limbs);
1723 }
1724 }
1725
1726 /// operands must be integers; handles undefined.
1727 pub fn bitwiseXor(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1728 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1729
1730 // TODO is this a performance issue? maybe we should try the operation without
1731 // resorting to BigInt first.
1732 var lhs_space: Value.BigIntSpace = undefined;
1733 var rhs_space: Value.BigIntSpace = undefined;
1734 const lhs_bigint = lhs.toBigInt(&lhs_space);
1735 const rhs_bigint = rhs.toBigInt(&rhs_space);
1736 const limbs = try arena.alloc(
1737 std.math.big.Limb,
1738 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1739 );
1740 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1741 result_bigint.bitXor(lhs_bigint, rhs_bigint);
1742 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1743
1744 if (result_bigint.positive) {
1745 return Value.Tag.int_big_positive.create(arena, result_limbs);
1746 } else {
1747 return Value.Tag.int_big_negative.create(arena, result_limbs);
1748 }
1749 }
1750
1527 pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {1751 pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
1528 // TODO is this a performance issue? maybe we should try the operation without1752 // TODO is this a performance issue? maybe we should try the operation without
1529 // resorting to BigInt first.1753 // resorting to BigInt first.
test/behavior/atomics.zig+29
...@@ -138,3 +138,32 @@ test "atomic store" {...@@ -138,3 +138,32 @@ test "atomic store" {
138 @atomicStore(u32, &x, 12345678, .SeqCst);138 @atomicStore(u32, &x, 12345678, .SeqCst);
139 try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);139 try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
140}140}
141
142test "atomic store comptime" {
143 comptime try testAtomicStore();
144 try testAtomicStore();
145}
146
147fn testAtomicStore() !void {
148 var x: u32 = 0;
149 @atomicStore(u32, &x, 1, .SeqCst);
150 try expect(@atomicLoad(u32, &x, .SeqCst) == 1);
151 @atomicStore(u32, &x, 12345678, .SeqCst);
152 try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
153}
154
155test "atomicrmw with floats" {
156 try testAtomicRmwFloat();
157 comptime try testAtomicRmwFloat();
158}
159
160fn testAtomicRmwFloat() !void {
161 var x: f32 = 0;
162 try expect(x == 0);
163 _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst);
164 try expect(x == 1);
165 _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst);
166 try expect(x == 6);
167 _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst);
168 try expect(x == 4);
169}
test/behavior/atomics_stage1.zig-29
...@@ -3,35 +3,6 @@ const expect = std.testing.expect;...@@ -3,35 +3,6 @@ const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
4const builtin = @import("builtin");4const builtin = @import("builtin");
55
6test "atomic store comptime" {
7 comptime try testAtomicStore();
8 try testAtomicStore();
9}
10
11fn testAtomicStore() !void {
12 var x: u32 = 0;
13 @atomicStore(u32, &x, 1, .SeqCst);
14 try expect(@atomicLoad(u32, &x, .SeqCst) == 1);
15 @atomicStore(u32, &x, 12345678, .SeqCst);
16 try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
17}
18
19test "atomicrmw with floats" {
20 try testAtomicRmwFloat();
21 comptime try testAtomicRmwFloat();
22}
23
24fn testAtomicRmwFloat() !void {
25 var x: f32 = 0;
26 try expect(x == 0);
27 _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst);
28 try expect(x == 1);
29 _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst);
30 try expect(x == 6);
31 _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst);
32 try expect(x == 4);
33}
34
35test "atomicrmw with ints" {6test "atomicrmw with ints" {
36 try testAtomicRmwInt();7 try testAtomicRmwInt();
37 comptime try testAtomicRmwInt();8 comptime try testAtomicRmwInt();