| author | |
| committer | |
| log | b9d3527e0ed53c4796ab64b4df7daf0909739807 |
| tree | 6cb29ce60e606fc4cfd30dad8b6f88c07a6bf8a7 |
| parent | 5dc251747b4ea65b1c8d3f2b5af62ca83c6d1196 |
* 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 | 311 | /// Given a pointer to an array, return a slice. |
| 312 | 312 | /// Uses the `ty_op` field. |
| 313 | 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, | |
| 314 | 320 | |
| 315 | 321 | /// Uses the `ty_pl` field with payload `Cmpxchg`. |
| 316 | 322 | cmpxchg_weak, |
| ... | ... | @@ -598,6 +604,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 598 | 604 | .struct_field_ptr_index_2, |
| 599 | 605 | .struct_field_ptr_index_3, |
| 600 | 606 | .array_to_slice, |
| 607 | .float_to_int, | |
| 608 | .int_to_float, | |
| 601 | 609 | => return air.getRefType(datas[inst].ty_op.ty), |
| 602 | 610 | |
| 603 | 611 | .loop, |
src/Liveness.zig+2| ... | ... | @@ -293,6 +293,8 @@ fn analyzeInst( |
| 293 | 293 | .struct_field_ptr_index_2, |
| 294 | 294 | .struct_field_ptr_index_3, |
| 295 | 295 | .array_to_slice, |
| 296 | .float_to_int, | |
| 297 | .int_to_float, | |
| 296 | 298 | => { |
| 297 | 299 | const o = inst_datas[inst].ty_op; |
| 298 | 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 | 4830 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 4831 | 4831 | defer arena.deinit(); |
| 4832 | 4832 | |
| 4833 | const min_int = try operand_ty.minInt(&arena, mod.getTarget()); | |
| 4834 | const max_int = try operand_ty.maxInt(&arena, mod.getTarget()); | |
| 4833 | const min_int = try operand_ty.minInt(&arena.allocator, mod.getTarget()); | |
| 4834 | const max_int = try operand_ty.maxInt(&arena.allocator, mod.getTarget()); | |
| 4835 | 4835 | if (try range_set.spans(min_int, max_int, operand_ty)) { |
| 4836 | 4836 | if (special_prong == .@"else") { |
| 4837 | 4837 | return mod.fail( |
| ... | ... | @@ -5671,10 +5671,13 @@ fn zirBitwise( |
| 5671 | 5671 | |
| 5672 | 5672 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { |
| 5673 | 5673 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { |
| 5674 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | |
| 5675 | return sema.addConstUndef(resolved_type); | |
| 5676 | } | |
| 5677 | return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{}); | |
| 5674 | const result_val = switch (air_tag) { | |
| 5675 | .bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena), | |
| 5676 | .bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena), | |
| 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 | } |
| 5680 | 5683 | |
| ... | ... | @@ -6028,8 +6031,8 @@ fn analyzeArithmetic( |
| 6028 | 6031 | } |
| 6029 | 6032 | |
| 6030 | 6033 | if (zir_tag == .mod_rem) { |
| 6031 | const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isFloat(); | |
| 6032 | const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isFloat(); | |
| 6034 | const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isRuntimeFloat(); | |
| 6035 | const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isRuntimeFloat(); | |
| 6033 | 6036 | if (dirty_lhs or dirty_rhs) { |
| 6034 | 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 | 7301 | fn zirFloatToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7299 | 7302 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 7300 | 7303 | const src = inst_data.src(); |
| 7304 | // TODO don't forget the safety check! | |
| 7301 | 7305 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{}); |
| 7302 | 7306 | } |
| 7303 | 7307 | |
| 7304 | 7308 | fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7305 | 7309 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 7306 | const src = inst_data.src(); | |
| 7307 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToFloat", .{}); | |
| 7310 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | |
| 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 | } |
| 7309 | 7329 | |
| 7310 | 7330 | 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 |
| 7542 | 7562 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{}); |
| 7543 | 7563 | } |
| 7544 | 7564 | |
| 7565 | fn 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 | ||
| 7579 | fn 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 | ||
| 7545 | 7593 | fn checkAtomicOperandType( |
| 7546 | 7594 | sema: *Sema, |
| 7547 | 7595 | block: *Scope.Block, |
| ... | ... | @@ -7815,9 +7863,23 @@ fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE |
| 7815 | 7863 | |
| 7816 | 7864 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { |
| 7817 | 7865 | if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { |
| 7818 | _ = ptr_val; | |
| 7819 | _ = operand_val; | |
| 7820 | return mod.fail(&block.base, src, "TODO implement Sema for @atomicRmw at comptime", .{}); | |
| 7866 | const target = sema.mod.getTarget(); | |
| 7867 | const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src; | |
| 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 | 7883 | } else break :rs operand_src; |
| 7822 | 7884 | } else ptr_src; |
| 7823 | 7885 | |
| ... | ... | @@ -9298,33 +9360,38 @@ fn coerceNum( |
| 9298 | 9360 | |
| 9299 | 9361 | const target = sema.mod.getTarget(); |
| 9300 | 9362 | |
| 9301 | if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { | |
| 9302 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | |
| 9303 | if (val.floatHasFraction()) { | |
| 9304 | return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty }); | |
| 9363 | switch (dst_zig_tag) { | |
| 9364 | .ComptimeInt, .Int => { | |
| 9365 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | |
| 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", .{}); | |
| 9307 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | |
| 9308 | if (!val.intFitsInType(dest_type, target)) { | |
| 9309 | return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val }); | |
| 9376 | }, | |
| 9377 | .ComptimeFloat, .Float => { | |
| 9378 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | |
| 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); | |
| 9312 | } | |
| 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 | } | |
| 9393 | }, | |
| 9394 | else => {}, | |
| 9328 | 9395 | } |
| 9329 | 9396 | return null; |
| 9330 | 9397 | } |
| ... | ... | @@ -9375,42 +9442,10 @@ fn storePtr2( |
| 9375 | 9442 | return; |
| 9376 | 9443 | |
| 9377 | 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| { | |
| 9379 | const const_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse | |
| 9380 | return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{}); | |
| 9381 | ||
| 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(); | |
| 9445 | const operand_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse | |
| 9446 | return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{}); | |
| 9447 | if (ptr_val.tag() == .decl_ref_mut) { | |
| 9448 | try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); | |
| 9414 | 9449 | return; |
| 9415 | 9450 | } |
| 9416 | 9451 | break :rs operand_src; |
| ... | ... | @@ -9422,6 +9457,53 @@ fn storePtr2( |
| 9422 | 9457 | _ = try block.addBinOp(air_tag, ptr, operand); |
| 9423 | 9458 | } |
| 9424 | 9459 | |
| 9460 | /// Call when you have Value objects rather than Air instructions, and you want to | |
| 9461 | /// assert the store must be done at comptime. | |
| 9462 | fn 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 | ||
| 9425 | 9507 | fn bitcast( |
| 9426 | 9508 | sema: *Sema, |
| 9427 | 9509 | block: *Scope.Block, |
| ... | ... | @@ -9801,11 +9883,11 @@ fn cmpNumeric( |
| 9801 | 9883 | const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| |
| 9802 | 9884 | lhs_val.compareWithZero(.lt) |
| 9803 | 9885 | else |
| 9804 | (lhs_ty.isFloat() or lhs_ty.isSignedInt()); | |
| 9886 | (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt()); | |
| 9805 | 9887 | const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| |
| 9806 | 9888 | rhs_val.compareWithZero(.lt) |
| 9807 | 9889 | else |
| 9808 | (rhs_ty.isFloat() or rhs_ty.isSignedInt()); | |
| 9890 | (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt()); | |
| 9809 | 9891 | const dest_int_is_signed = lhs_is_signed or rhs_is_signed; |
| 9810 | 9892 | |
| 9811 | 9893 | var dest_float_type: ?Type = null; |
| ... | ... | @@ -10031,7 +10113,7 @@ fn resolvePeerTypes( |
| 10031 | 10113 | } |
| 10032 | 10114 | continue; |
| 10033 | 10115 | } |
| 10034 | if (chosen_ty.isFloat() and candidate_ty.isFloat()) { | |
| 10116 | if (chosen_ty.isRuntimeFloat() and candidate_ty.isRuntimeFloat()) { | |
| 10035 | 10117 | if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) { |
| 10036 | 10118 | chosen = candidate; |
| 10037 | 10119 | chosen_i = candidate_i + 1; |
| ... | ... | @@ -10049,13 +10131,13 @@ fn resolvePeerTypes( |
| 10049 | 10131 | continue; |
| 10050 | 10132 | } |
| 10051 | 10133 | |
| 10052 | if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isFloat()) { | |
| 10134 | if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isRuntimeFloat()) { | |
| 10053 | 10135 | chosen = candidate; |
| 10054 | 10136 | chosen_i = candidate_i + 1; |
| 10055 | 10137 | continue; |
| 10056 | 10138 | } |
| 10057 | 10139 | |
| 10058 | if (chosen_ty.isFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) { | |
| 10140 | if (chosen_ty.isRuntimeFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) { | |
| 10059 | 10141 | continue; |
| 10060 | 10142 | } |
| 10061 | 10143 |
src/codegen.zig+22| ... | ... | @@ -858,6 +858,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 858 | 858 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 859 | 859 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 860 | 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 | 863 | .cmpxchg_strong => try self.airCmpxchg(inst), |
| 862 | 864 | .cmpxchg_weak => try self.airCmpxchg(inst), |
| 863 | 865 | .atomic_rmw => try self.airAtomicRmw(inst), |
| ... | ... | @@ -4769,6 +4771,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4769 | 4771 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 4770 | 4772 | } |
| 4771 | 4773 | |
| 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 | 4794 | fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { |
| 4773 | 4795 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 4774 | 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 | 917 | .atomic_rmw => try airAtomicRmw(o, inst), |
| 918 | 918 | .atomic_load => try airAtomicLoad(o, inst), |
| 919 | 919 | |
| 920 | .int_to_float, .float_to_int => try airSimpleCast(o, inst), | |
| 921 | ||
| 920 | 922 | .atomic_store_unordered => try airAtomicStore(o, inst, toMemoryOrder(.Unordered)), |
| 921 | 923 | .atomic_store_monotonic => try airAtomicStore(o, inst, toMemoryOrder(.Monotonic)), |
| 922 | 924 | .atomic_store_release => try airAtomicStore(o, inst, toMemoryOrder(.Release)), |
| ... | ... | @@ -1899,6 +1901,24 @@ fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1899 | 1901 | return local; |
| 1900 | 1902 | } |
| 1901 | 1903 | |
| 1904 | /// Emits a local variable with the result type and initializes it | |
| 1905 | /// with the operand. | |
| 1906 | fn 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 | ||
| 1902 | 1922 | fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { |
| 1903 | 1923 | const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; |
| 1904 | 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 | 1122 | .slice_ptr => try self.airSliceField(inst, 0), |
| 1123 | 1123 | .slice_len => try self.airSliceField(inst, 1), |
| 1124 | 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 | 1127 | .cmpxchg_weak => try self.airCmpxchg(inst, true), |
| 1126 | 1128 | .cmpxchg_strong => try self.airCmpxchg(inst, false), |
| 1127 | 1129 | .fence => try self.airFence(inst), |
| ... | ... | @@ -1372,6 +1374,40 @@ pub const FuncGen = struct { |
| 1372 | 1374 | return self.builder.buildInsertValue(partial, len, 1, ""); |
| 1373 | 1375 | } |
| 1374 | 1376 | |
| 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 | 1411 | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value { |
| 1376 | 1412 | if (self.liveness.isUnused(inst)) |
| 1377 | 1413 | return null; |
| ... | ... | @@ -1818,7 +1854,7 @@ pub const FuncGen = struct { |
| 1818 | 1854 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1819 | 1855 | const inst_ty = self.air.typeOfIndex(inst); |
| 1820 | 1856 | |
| 1821 | if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, ""); | |
| 1857 | if (inst_ty.isRuntimeFloat()) return self.builder.buildFAdd(lhs, rhs, ""); | |
| 1822 | 1858 | if (wrap) return self.builder.buildAdd(lhs, rhs, ""); |
| 1823 | 1859 | if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); |
| 1824 | 1860 | return self.builder.buildNUWAdd(lhs, rhs, ""); |
| ... | ... | @@ -1833,7 +1869,7 @@ pub const FuncGen = struct { |
| 1833 | 1869 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1834 | 1870 | const inst_ty = self.air.typeOfIndex(inst); |
| 1835 | 1871 | |
| 1836 | if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, ""); | |
| 1872 | if (inst_ty.isRuntimeFloat()) return self.builder.buildFSub(lhs, rhs, ""); | |
| 1837 | 1873 | if (wrap) return self.builder.buildSub(lhs, rhs, ""); |
| 1838 | 1874 | if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); |
| 1839 | 1875 | return self.builder.buildNUWSub(lhs, rhs, ""); |
| ... | ... | @@ -1848,7 +1884,7 @@ pub const FuncGen = struct { |
| 1848 | 1884 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1849 | 1885 | const inst_ty = self.air.typeOfIndex(inst); |
| 1850 | 1886 | |
| 1851 | if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, ""); | |
| 1887 | if (inst_ty.isRuntimeFloat()) return self.builder.buildFMul(lhs, rhs, ""); | |
| 1852 | 1888 | if (wrap) return self.builder.buildMul(lhs, rhs, ""); |
| 1853 | 1889 | if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); |
| 1854 | 1890 | return self.builder.buildNUWMul(lhs, rhs, ""); |
| ... | ... | @@ -1863,7 +1899,7 @@ pub const FuncGen = struct { |
| 1863 | 1899 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1864 | 1900 | const inst_ty = self.air.typeOfIndex(inst); |
| 1865 | 1901 | |
| 1866 | if (inst_ty.isFloat()) return self.builder.buildFDiv(lhs, rhs, ""); | |
| 1902 | if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, ""); | |
| 1867 | 1903 | if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); |
| 1868 | 1904 | return self.builder.buildUDiv(lhs, rhs, ""); |
| 1869 | 1905 | } |
| ... | ... | @@ -1876,7 +1912,7 @@ pub const FuncGen = struct { |
| 1876 | 1912 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1877 | 1913 | const inst_ty = self.air.typeOfIndex(inst); |
| 1878 | 1914 | |
| 1879 | if (inst_ty.isFloat()) return self.builder.buildFRem(lhs, rhs, ""); | |
| 1915 | if (inst_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, ""); | |
| 1880 | 1916 | if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); |
| 1881 | 1917 | return self.builder.buildURem(lhs, rhs, ""); |
| 1882 | 1918 | } |
| ... | ... | @@ -2165,7 +2201,7 @@ pub const FuncGen = struct { |
| 2165 | 2201 | const operand_ty = ptr_ty.elemType(); |
| 2166 | 2202 | const operand = try self.resolveInst(extra.operand); |
| 2167 | 2203 | const is_signed_int = operand_ty.isSignedInt(); |
| 2168 | const is_float = operand_ty.isFloat(); | |
| 2204 | const is_float = operand_ty.isRuntimeFloat(); | |
| 2169 | 2205 | const op = toLlvmAtomicRmwBinOp(extra.op(), is_signed_int, is_float); |
| 2170 | 2206 | const ordering = toLlvmAtomicOrdering(extra.ordering()); |
| 2171 | 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 | 563 | ordering: AtomicOrdering, |
| 564 | 564 | singleThread: Bool, |
| 565 | 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 | }; |
| 567 | 599 | |
| 568 | 600 | pub const IntPredicate = enum(c_uint) { |
src/print_air.zig+2| ... | ... | @@ -175,6 +175,8 @@ const Writer = struct { |
| 175 | 175 | .struct_field_ptr_index_2, |
| 176 | 176 | .struct_field_ptr_index_3, |
| 177 | 177 | .array_to_slice, |
| 178 | .int_to_float, | |
| 179 | .float_to_int, | |
| 178 | 180 | => try w.writeTyOp(s, inst), |
| 179 | 181 | |
| 180 | 182 | .block, |
src/type.zig+31-14| ... | ... | @@ -2523,7 +2523,8 @@ pub const Type = extern union { |
| 2523 | 2523 | }; |
| 2524 | 2524 | } |
| 2525 | 2525 | |
| 2526 | pub fn isFloat(self: Type) bool { | |
| 2526 | /// Returns `false` for `comptime_float`. | |
| 2527 | pub fn isRuntimeFloat(self: Type) bool { | |
| 2527 | 2528 | return switch (self.tag()) { |
| 2528 | 2529 | .f16, |
| 2529 | 2530 | .f32, |
| ... | ... | @@ -2536,13 +2537,29 @@ pub const Type = extern union { |
| 2536 | 2537 | }; |
| 2537 | 2538 | } |
| 2538 | 2539 | |
| 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 | 2557 | pub fn floatBits(self: Type, target: Target) u16 { |
| 2541 | 2558 | return switch (self.tag()) { |
| 2542 | 2559 | .f16 => 16, |
| 2543 | 2560 | .f32 => 32, |
| 2544 | 2561 | .f64 => 64, |
| 2545 | .f128 => 128, | |
| 2562 | .f128, .comptime_float => 128, | |
| 2546 | 2563 | .c_longdouble => CType.longdouble.sizeInBits(target), |
| 2547 | 2564 | |
| 2548 | 2565 | else => unreachable, |
| ... | ... | @@ -2879,7 +2896,7 @@ pub const Type = extern union { |
| 2879 | 2896 | } |
| 2880 | 2897 | |
| 2881 | 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 | 2900 | assert(self.zigTypeTag() == .Int); |
| 2884 | 2901 | const info = self.intInfo(target); |
| 2885 | 2902 | |
| ... | ... | @@ -2889,35 +2906,35 @@ pub const Type = extern union { |
| 2889 | 2906 | |
| 2890 | 2907 | if ((info.bits - 1) <= std.math.maxInt(u6)) { |
| 2891 | 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 | } |
| 2894 | 2911 | |
| 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 | 2913 | try res.shiftLeft(res, info.bits - 1); |
| 2897 | 2914 | res.negate(); |
| 2898 | 2915 | |
| 2899 | 2916 | const res_const = res.toConst(); |
| 2900 | 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 | 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 | } |
| 2906 | 2923 | |
| 2907 | 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 | 2926 | assert(self.zigTypeTag() == .Int); |
| 2910 | 2927 | const info = self.intInfo(target); |
| 2911 | 2928 | |
| 2912 | 2929 | if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) { |
| 2913 | 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 | 2932 | } else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) { |
| 2916 | 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 | } |
| 2919 | 2936 | |
| 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 | 2938 | try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed)); |
| 2922 | 2939 | const one = std.math.big.int.Const{ |
| 2923 | 2940 | .limbs = &[_]std.math.big.Limb{1}, |
| ... | ... | @@ -2927,9 +2944,9 @@ pub const Type = extern union { |
| 2927 | 2944 | |
| 2928 | 2945 | const res_const = res.toConst(); |
| 2929 | 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 | 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 | } |
| 2935 | 2952 |
src/value.zig+224| ... | ... | @@ -1524,6 +1524,230 @@ pub const Value = extern union { |
| 1524 | 1524 | }; |
| 1525 | 1525 | } |
| 1526 | 1526 | |
| 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 | 1751 | pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value { |
| 1528 | 1752 | // TODO is this a performance issue? maybe we should try the operation without |
| 1529 | 1753 | // resorting to BigInt first. |
test/behavior/atomics.zig+29| ... | ... | @@ -138,3 +138,32 @@ test "atomic store" { |
| 138 | 138 | @atomicStore(u32, &x, 12345678, .SeqCst); |
| 139 | 139 | try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); |
| 140 | 140 | } |
| 141 | ||
| 142 | test "atomic store comptime" { | |
| 143 | comptime try testAtomicStore(); | |
| 144 | try testAtomicStore(); | |
| 145 | } | |
| 146 | ||
| 147 | fn 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 | ||
| 155 | test "atomicrmw with floats" { | |
| 156 | try testAtomicRmwFloat(); | |
| 157 | comptime try testAtomicRmwFloat(); | |
| 158 | } | |
| 159 | ||
| 160 | fn 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 | 3 | const expectEqual = std.testing.expectEqual; |
| 4 | 4 | const builtin = @import("builtin"); |
| 5 | 5 | |
| 6 | test "atomic store comptime" { | |
| 7 | comptime try testAtomicStore(); | |
| 8 | try testAtomicStore(); | |
| 9 | } | |
| 10 | ||
| 11 | fn 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 | ||
| 19 | test "atomicrmw with floats" { | |
| 20 | try testAtomicRmwFloat(); | |
| 21 | comptime try testAtomicRmwFloat(); | |
| 22 | } | |
| 23 | ||
| 24 | fn 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 | ||
| 35 | 6 | test "atomicrmw with ints" { |
| 36 | 7 | try testAtomicRmwInt(); |
| 37 | 8 | comptime try testAtomicRmwInt(); |