| author | |
| committer | |
| log | b9a95f2dd94e6175322d3388c3936eb600ec90ea |
| tree | 186a31fd920ca47bc15c3a426b9dfa8d67756ada |
| parent | 29f41896ed9d99e82a88f4b63efa182ca0d2f93c |
- modify AstGen binOpExt()/assignBinOpExt() to accept generic extended payload T
- rework Sema zirSatArithmetic() to use existing sema.analyzeArithmetic() by adding an `opt_extended` parameter.
- add airSatOp() to codegen/c.zig
- add saturating functions to src/link/C/zig.h5 files changed, 262 insertions(+), 44 deletions(-)
src/AstGen.zig+32-30| ... | @@ -535,7 +535,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr | ... | @@ -535,7 +535,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 535 | return rvalue(gz, rl, .void_value, node); | 535 | return rvalue(gz, rl, .void_value, node); |
| 536 | }, | 536 | }, |
| 537 | .assign_bit_shift_left_sat => { | 537 | .assign_bit_shift_left_sat => { |
| 538 | try assignBinOpExt(gz, scope, node, .shl_with_saturation); | 538 | try assignBinOpExt(gz, scope, node, .shl_with_saturation, Zir.Inst.SaturatingArithmetic); |
| 539 | return rvalue(gz, rl, .void_value, node); | 539 | return rvalue(gz, rl, .void_value, node); |
| 540 | }, | 540 | }, |
| 541 | .assign_bit_shift_right => { | 541 | .assign_bit_shift_right => { |
| ... | @@ -568,7 +568,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr | ... | @@ -568,7 +568,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 568 | return rvalue(gz, rl, .void_value, node); | 568 | return rvalue(gz, rl, .void_value, node); |
| 569 | }, | 569 | }, |
| 570 | .assign_sub_sat => { | 570 | .assign_sub_sat => { |
| 571 | try assignBinOpExt(gz, scope, node, .sub_with_saturation); | 571 | try assignBinOpExt(gz, scope, node, .sub_with_saturation, Zir.Inst.SaturatingArithmetic); |
| 572 | return rvalue(gz, rl, .void_value, node); | 572 | return rvalue(gz, rl, .void_value, node); |
| 573 | }, | 573 | }, |
| 574 | .assign_mod => { | 574 | .assign_mod => { |
| ... | @@ -584,7 +584,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr | ... | @@ -584,7 +584,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 584 | return rvalue(gz, rl, .void_value, node); | 584 | return rvalue(gz, rl, .void_value, node); |
| 585 | }, | 585 | }, |
| 586 | .assign_add_sat => { | 586 | .assign_add_sat => { |
| 587 | try assignBinOpExt(gz, scope, node, .add_with_saturation); | 587 | try assignBinOpExt(gz, scope, node, .add_with_saturation, Zir.Inst.SaturatingArithmetic); |
| 588 | return rvalue(gz, rl, .void_value, node); | 588 | return rvalue(gz, rl, .void_value, node); |
| 589 | }, | 589 | }, |
| 590 | .assign_mul => { | 590 | .assign_mul => { |
| ... | @@ -596,24 +596,24 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr | ... | @@ -596,24 +596,24 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 596 | return rvalue(gz, rl, .void_value, node); | 596 | return rvalue(gz, rl, .void_value, node); |
| 597 | }, | 597 | }, |
| 598 | .assign_mul_sat => { | 598 | .assign_mul_sat => { |
| 599 | try assignBinOpExt(gz, scope, node, .mul_with_saturation); | 599 | try assignBinOpExt(gz, scope, node, .mul_with_saturation, Zir.Inst.SaturatingArithmetic); |
| 600 | return rvalue(gz, rl, .void_value, node); | 600 | return rvalue(gz, rl, .void_value, node); |
| 601 | }, | 601 | }, |
| 602 | 602 | ||
| 603 | // zig fmt: off | 603 | // zig fmt: off |
| 604 | .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl), | 604 | .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl), |
| 605 | .bit_shift_left_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl_with_saturation), | 605 | .bit_shift_left_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl_with_saturation, Zir.Inst.SaturatingArithmetic), |
| 606 | .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr), | 606 | .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr), |
| 607 | 607 | ||
| 608 | .add => return simpleBinOp(gz, scope, rl, node, .add), | 608 | .add => return simpleBinOp(gz, scope, rl, node, .add), |
| 609 | .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap), | 609 | .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap), |
| 610 | .add_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .add_with_saturation), | 610 | .add_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .add_with_saturation, Zir.Inst.SaturatingArithmetic), |
| 611 | .sub => return simpleBinOp(gz, scope, rl, node, .sub), | 611 | .sub => return simpleBinOp(gz, scope, rl, node, .sub), |
| 612 | .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap), | 612 | .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap), |
| 613 | .sub_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .sub_with_saturation), | 613 | .sub_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .sub_with_saturation, Zir.Inst.SaturatingArithmetic), |
| 614 | .mul => return simpleBinOp(gz, scope, rl, node, .mul), | 614 | .mul => return simpleBinOp(gz, scope, rl, node, .mul), |
| 615 | .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap), | 615 | .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap), |
| 616 | .mul_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .mul_with_saturation), | 616 | .mul_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .mul_with_saturation, Zir.Inst.SaturatingArithmetic), |
| 617 | .div => return simpleBinOp(gz, scope, rl, node, .div), | 617 | .div => return simpleBinOp(gz, scope, rl, node, .div), |
| 618 | .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem), | 618 | .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem), |
| 619 | .bit_and => { | 619 | .bit_and => { |
| ... | @@ -2713,6 +2713,28 @@ fn assignOp( | ... | @@ -2713,6 +2713,28 @@ fn assignOp( |
| 2713 | _ = try gz.addBin(.store, lhs_ptr, result); | 2713 | _ = try gz.addBin(.store, lhs_ptr, result); |
| 2714 | } | 2714 | } |
| 2715 | 2715 | ||
| 2716 | // TODO: is there an existing way to do this? | ||
| 2717 | // TODO: likely rename this to reflect result_loc == .none or add more params to make it more general | ||
| 2718 | fn binOpExt( | ||
| 2719 | gz: *GenZir, | ||
| 2720 | scope: *Scope, | ||
| 2721 | rl: ResultLoc, | ||
| 2722 | infix_node: Ast.Node.Index, | ||
| 2723 | lhs_node: Ast.Node.Index, | ||
| 2724 | rhs_node: Ast.Node.Index, | ||
| 2725 | tag: Zir.Inst.Extended, | ||
| 2726 | comptime T: type, | ||
| 2727 | ) InnerError!Zir.Inst.Ref { | ||
| 2728 | const lhs = try expr(gz, scope, .none, lhs_node); | ||
| 2729 | const rhs = try expr(gz, scope, .none, rhs_node); | ||
| 2730 | const result = try gz.addExtendedPayload(tag, T{ | ||
| 2731 | .node = gz.nodeIndexToRelative(infix_node), | ||
| 2732 | .lhs = lhs, | ||
| 2733 | .rhs = rhs, | ||
| 2734 | }); | ||
| 2735 | return rvalue(gz, rl, result, infix_node); | ||
| 2736 | } | ||
| 2737 | |||
| 2716 | // TODO: is there an existing method to accomplish this? | 2738 | // TODO: is there an existing method to accomplish this? |
| 2717 | // TODO: likely rename this to indicate rhs type coercion or add more params to make it more general | 2739 | // TODO: likely rename this to indicate rhs type coercion or add more params to make it more general |
| 2718 | fn assignBinOpExt( | 2740 | fn assignBinOpExt( |
| ... | @@ -2720,8 +2742,8 @@ fn assignBinOpExt( | ... | @@ -2720,8 +2742,8 @@ fn assignBinOpExt( |
| 2720 | scope: *Scope, | 2742 | scope: *Scope, |
| 2721 | infix_node: Ast.Node.Index, | 2743 | infix_node: Ast.Node.Index, |
| 2722 | op_inst_tag: Zir.Inst.Extended, | 2744 | op_inst_tag: Zir.Inst.Extended, |
| 2745 | comptime T: type, | ||
| 2723 | ) InnerError!void { | 2746 | ) InnerError!void { |
| 2724 | try emitDbgNode(gz, infix_node); | ||
| 2725 | const astgen = gz.astgen; | 2747 | const astgen = gz.astgen; |
| 2726 | const tree = astgen.tree; | 2748 | const tree = astgen.tree; |
| 2727 | const node_datas = tree.nodes.items(.data); | 2749 | const node_datas = tree.nodes.items(.data); |
| ... | @@ -2730,7 +2752,7 @@ fn assignBinOpExt( | ... | @@ -2730,7 +2752,7 @@ fn assignBinOpExt( |
| 2730 | const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); | 2752 | const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); |
| 2731 | const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node); | 2753 | const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node); |
| 2732 | const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs); | 2754 | const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs); |
| 2733 | const result = try gz.addExtendedPayload(op_inst_tag, Zir.Inst.BinNode{ | 2755 | const result = try gz.addExtendedPayload(op_inst_tag, T{ |
| 2734 | .node = gz.nodeIndexToRelative(infix_node), | 2756 | .node = gz.nodeIndexToRelative(infix_node), |
| 2735 | .lhs = lhs, | 2757 | .lhs = lhs, |
| 2736 | .rhs = rhs, | 2758 | .rhs = rhs, |
| ... | @@ -7903,26 +7925,6 @@ fn shiftOp( | ... | @@ -7903,26 +7925,6 @@ fn shiftOp( |
| 7903 | return rvalue(gz, rl, result, node); | 7925 | return rvalue(gz, rl, result, node); |
| 7904 | } | 7926 | } |
| 7905 | 7927 | ||
| 7906 | // TODO: is there an existing way to do this? | ||
| 7907 | // TODO: likely rename this to reflect result_loc == .none or add more params to make it more general | ||
| 7908 | fn binOpExt( | ||
| 7909 | gz: *GenZir, | ||
| 7910 | scope: *Scope, | ||
| 7911 | rl: ResultLoc, | ||
| 7912 | node: Ast.Node.Index, | ||
| 7913 | lhs_node: Ast.Node.Index, | ||
| 7914 | rhs_node: Ast.Node.Index, | ||
| 7915 | tag: Zir.Inst.Extended, | ||
| 7916 | ) InnerError!Zir.Inst.Ref { | ||
| 7917 | const lhs = try expr(gz, scope, .none, lhs_node); | ||
| 7918 | const rhs = try expr(gz, scope, .none, rhs_node); | ||
| 7919 | const result = try gz.addExtendedPayload(tag, Zir.Inst.Bin{ | ||
| 7920 | .lhs = lhs, | ||
| 7921 | .rhs = rhs, | ||
| 7922 | }); | ||
| 7923 | return rvalue(gz, rl, result, node); | ||
| 7924 | } | ||
| 7925 | |||
| 7926 | fn cImport( | 7928 | fn cImport( |
| 7927 | gz: *GenZir, | 7929 | gz: *GenZir, |
| 7928 | scope: *Scope, | 7930 | scope: *Scope, |
src/Sema.zig+17-8| ... | @@ -694,10 +694,11 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -694,10 +694,11 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 694 | .c_define => return sema.zirCDefine( block, extended), | 694 | .c_define => return sema.zirCDefine( block, extended), |
| 695 | .wasm_memory_size => return sema.zirWasmMemorySize( block, extended), | 695 | .wasm_memory_size => return sema.zirWasmMemorySize( block, extended), |
| 696 | .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended), | 696 | .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended), |
| 697 | .add_with_saturation=> return sema.zirSatArithmetic( block, extended), | 697 | .add_with_saturation, |
| 698 | .sub_with_saturation=> return sema.zirSatArithmetic( block, extended), | 698 | .sub_with_saturation, |
| 699 | .mul_with_saturation=> return sema.zirSatArithmetic( block, extended), | 699 | .mul_with_saturation, |
| 700 | .shl_with_saturation=> return sema.zirSatArithmetic( block, extended), | 700 | .shl_with_saturation, |
| 701 | => return sema.zirSatArithmetic( block, extended), | ||
| 701 | // zig fmt: on | 702 | // zig fmt: on |
| 702 | } | 703 | } |
| 703 | } | 704 | } |
| ... | @@ -6163,7 +6164,7 @@ fn zirNegate( | ... | @@ -6163,7 +6164,7 @@ fn zirNegate( |
| 6163 | const lhs = sema.resolveInst(.zero); | 6164 | const lhs = sema.resolveInst(.zero); |
| 6164 | const rhs = sema.resolveInst(inst_data.operand); | 6165 | const rhs = sema.resolveInst(inst_data.operand); |
| 6165 | 6166 | ||
| 6166 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); | 6167 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src, null); |
| 6167 | } | 6168 | } |
| 6168 | 6169 | ||
| 6169 | fn zirArithmetic( | 6170 | fn zirArithmetic( |
| ... | @@ -6183,7 +6184,7 @@ fn zirArithmetic( | ... | @@ -6183,7 +6184,7 @@ fn zirArithmetic( |
| 6183 | const lhs = sema.resolveInst(extra.lhs); | 6184 | const lhs = sema.resolveInst(extra.lhs); |
| 6184 | const rhs = sema.resolveInst(extra.rhs); | 6185 | const rhs = sema.resolveInst(extra.rhs); |
| 6185 | 6186 | ||
| 6186 | return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src); | 6187 | return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, null); |
| 6187 | } | 6188 | } |
| 6188 | 6189 | ||
| 6189 | fn zirOverflowArithmetic( | 6190 | fn zirOverflowArithmetic( |
| ... | @@ -6209,10 +6210,17 @@ fn zirSatArithmetic( | ... | @@ -6209,10 +6210,17 @@ fn zirSatArithmetic( |
| 6209 | defer tracy.end(); | 6210 | defer tracy.end(); |
| 6210 | 6211 | ||
| 6211 | const extra = sema.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data; | 6212 | const extra = sema.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data; |
| 6212 | const src: LazySrcLoc = .{ .node_offset = extra.node }; | 6213 | sema.src = .{ .node_offset_bin_op = extra.node }; |
| 6213 | return sema.mod.fail(&block.base, src, "TODO implement Sema.zirSatArithmetic", .{}); | 6214 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = extra.node }; |
| 6215 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = extra.node }; | ||
| 6216 | const lhs = sema.resolveInst(extra.lhs); | ||
| 6217 | const rhs = sema.resolveInst(extra.rhs); | ||
| 6218 | |||
| 6219 | return sema.analyzeArithmetic(block, .extended, lhs, rhs, sema.src, lhs_src, rhs_src, extended); | ||
| 6214 | } | 6220 | } |
| 6215 | 6221 | ||
| 6222 | // TODO: audit - not sure if its a good idea to reuse this, adding `opt_extended` param | ||
| 6223 | // FIXME: somehow, rhs of <<| is required to be Log2T. this should accept T | ||
| 6216 | fn analyzeArithmetic( | 6224 | fn analyzeArithmetic( |
| 6217 | sema: *Sema, | 6225 | sema: *Sema, |
| 6218 | block: *Scope.Block, | 6226 | block: *Scope.Block, |
| ... | @@ -6223,6 +6231,7 @@ fn analyzeArithmetic( | ... | @@ -6223,6 +6231,7 @@ fn analyzeArithmetic( |
| 6223 | src: LazySrcLoc, | 6231 | src: LazySrcLoc, |
| 6224 | lhs_src: LazySrcLoc, | 6232 | lhs_src: LazySrcLoc, |
| 6225 | rhs_src: LazySrcLoc, | 6233 | rhs_src: LazySrcLoc, |
| 6234 | opt_extended: ?Zir.Inst.Extended.InstData, | ||
| 6226 | ) CompileError!Air.Inst.Ref { | 6235 | ) CompileError!Air.Inst.Ref { |
| 6227 | const lhs_ty = sema.typeOf(lhs); | 6236 | const lhs_ty = sema.typeOf(lhs); |
| 6228 | const rhs_ty = sema.typeOf(rhs); | 6237 | const rhs_ty = sema.typeOf(rhs); |
src/codegen/c.zig+117-3| ... | @@ -885,17 +885,17 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -885,17 +885,17 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 885 | // that wrapping is UB. | 885 | // that wrapping is UB. |
| 886 | .add, .ptr_add => try airBinOp( f, inst, " + "), | 886 | .add, .ptr_add => try airBinOp( f, inst, " + "), |
| 887 | .addwrap => try airWrapOp(f, inst, " + ", "addw_"), | 887 | .addwrap => try airWrapOp(f, inst, " + ", "addw_"), |
| 888 | .addsat => return o.dg.fail("TODO: C backend: implement codegen for addsat", .{}), | 888 | .addsat => return f.fail("TODO: C backend: implement codegen for addsat", .{}), |
| 889 | // TODO use a different strategy for sub that communicates to the optimizer | 889 | // TODO use a different strategy for sub that communicates to the optimizer |
| 890 | // that wrapping is UB. | 890 | // that wrapping is UB. |
| 891 | .sub, .ptr_sub => try airBinOp( f, inst, " - "), | 891 | .sub, .ptr_sub => try airBinOp( f, inst, " - "), |
| 892 | .subwrap => try airWrapOp(f, inst, " - ", "subw_"), | 892 | .subwrap => try airWrapOp(f, inst, " - ", "subw_"), |
| 893 | .subsat => return o.dg.fail("TODO: C backend: implement codegen for subsat", .{}), | 893 | .subsat => return f.fail("TODO: C backend: implement codegen for subsat", .{}), |
| 894 | // TODO use a different strategy for mul that communicates to the optimizer | 894 | // TODO use a different strategy for mul that communicates to the optimizer |
| 895 | // that wrapping is UB. | 895 | // that wrapping is UB. |
| 896 | .mul => try airBinOp( f, inst, " * "), | 896 | .mul => try airBinOp( f, inst, " * "), |
| 897 | .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"), | 897 | .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"), |
| 898 | .mulsat => return o.dg.fail("TODO: C backend: implement codegen for mulsat", .{}), | 898 | .mulsat => return f.fail("TODO: C backend: implement codegen for mulsat", .{}), |
| 899 | // TODO use a different strategy for div that communicates to the optimizer | 899 | // TODO use a different strategy for div that communicates to the optimizer |
| 900 | // that wrapping is UB. | 900 | // that wrapping is UB. |
| 901 | .div => try airBinOp( f, inst, " / "), | 901 | .div => try airBinOp( f, inst, " / "), |
| ... | @@ -919,6 +919,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -919,6 +919,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 919 | 919 | ||
| 920 | .shr => try airBinOp(f, inst, " >> "), | 920 | .shr => try airBinOp(f, inst, " >> "), |
| 921 | .shl => try airBinOp(f, inst, " << "), | 921 | .shl => try airBinOp(f, inst, " << "), |
| 922 | .shl_sat => return f.fail("TODO: C backend: implement codegen for mulsat", .{}), | ||
| 923 | |||
| 922 | 924 | ||
| 923 | .not => try airNot( f, inst), | 925 | .not => try airNot( f, inst), |
| 924 | 926 | ||
| ... | @@ -1312,6 +1314,118 @@ fn airWrapOp( | ... | @@ -1312,6 +1314,118 @@ fn airWrapOp( |
| 1312 | return ret; | 1314 | return ret; |
| 1313 | } | 1315 | } |
| 1314 | 1316 | ||
| 1317 | fn airSatOp( | ||
| 1318 | o: *Object, | ||
| 1319 | inst: Air.Inst.Index, | ||
| 1320 | str_op: [*:0]const u8, | ||
| 1321 | fn_op: [*:0]const u8, | ||
| 1322 | ) !CValue { | ||
| 1323 | if (o.liveness.isUnused(inst)) | ||
| 1324 | return CValue.none; | ||
| 1325 | |||
| 1326 | const bin_op = o.air.instructions.items(.data)[inst].bin_op; | ||
| 1327 | const inst_ty = o.air.typeOfIndex(inst); | ||
| 1328 | const int_info = inst_ty.intInfo(o.dg.module.getTarget()); | ||
| 1329 | const bits = int_info.bits; | ||
| 1330 | |||
| 1331 | // if it's an unsigned int with non-arbitrary bit size then we can just add | ||
| 1332 | const ok_bits = switch (bits) { | ||
| 1333 | 8, 16, 32, 64, 128 => true, | ||
| 1334 | else => false, | ||
| 1335 | }; | ||
| 1336 | |||
| 1337 | if (bits > 64) { | ||
| 1338 | return f.fail("TODO: C backend: airSatOp for large integers", .{}); | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | var min_buf: [80]u8 = undefined; | ||
| 1342 | const min = switch (int_info.signedness) { | ||
| 1343 | .unsigned => "0", | ||
| 1344 | else => switch (inst_ty.tag()) { | ||
| 1345 | .c_short => "SHRT_MIN", | ||
| 1346 | .c_int => "INT_MIN", | ||
| 1347 | .c_long => "LONG_MIN", | ||
| 1348 | .c_longlong => "LLONG_MIN", | ||
| 1349 | .isize => "INTPTR_MIN", | ||
| 1350 | else => blk: { | ||
| 1351 | const val = -1 * std.math.pow(i65, 2, @intCast(i65, bits - 1)); | ||
| 1352 | break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { | ||
| 1353 | error.NoSpaceLeft => unreachable, | ||
| 1354 | else => |e| return e, | ||
| 1355 | }; | ||
| 1356 | }, | ||
| 1357 | }, | ||
| 1358 | }; | ||
| 1359 | |||
| 1360 | var max_buf: [80]u8 = undefined; | ||
| 1361 | const max = switch (inst_ty.tag()) { | ||
| 1362 | .c_short => "SHRT_MAX", | ||
| 1363 | .c_ushort => "USHRT_MAX", | ||
| 1364 | .c_int => "INT_MAX", | ||
| 1365 | .c_uint => "UINT_MAX", | ||
| 1366 | .c_long => "LONG_MAX", | ||
| 1367 | .c_ulong => "ULONG_MAX", | ||
| 1368 | .c_longlong => "LLONG_MAX", | ||
| 1369 | .c_ulonglong => "ULLONG_MAX", | ||
| 1370 | .isize => "INTPTR_MAX", | ||
| 1371 | .usize => "UINTPTR_MAX", | ||
| 1372 | else => blk: { | ||
| 1373 | const pow_bits = switch (int_info.signedness) { | ||
| 1374 | .signed => bits - 1, | ||
| 1375 | .unsigned => bits, | ||
| 1376 | }; | ||
| 1377 | const val = std.math.pow(u65, 2, pow_bits) - 1; | ||
| 1378 | break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { | ||
| 1379 | error.NoSpaceLeft => unreachable, | ||
| 1380 | else => |e| return e, | ||
| 1381 | }; | ||
| 1382 | }, | ||
| 1383 | }; | ||
| 1384 | |||
| 1385 | const lhs = try o.resolveInst(bin_op.lhs); | ||
| 1386 | const rhs = try o.resolveInst(bin_op.rhs); | ||
| 1387 | const w = o.writer(); | ||
| 1388 | |||
| 1389 | const ret = try o.allocLocal(inst_ty, .Mut); | ||
| 1390 | try w.print(" = zig_{s}", .{fn_op}); | ||
| 1391 | |||
| 1392 | switch (inst_ty.tag()) { | ||
| 1393 | .isize => try w.writeAll("isize"), | ||
| 1394 | .c_short => try w.writeAll("short"), | ||
| 1395 | .c_int => try w.writeAll("int"), | ||
| 1396 | .c_long => try w.writeAll("long"), | ||
| 1397 | .c_longlong => try w.writeAll("longlong"), | ||
| 1398 | else => { | ||
| 1399 | const prefix_byte: u8 = switch (int_info.signedness) { | ||
| 1400 | .signed => 'i', | ||
| 1401 | .unsigned => 'u', | ||
| 1402 | }; | ||
| 1403 | for ([_]u8{ 8, 16, 32, 64 }) |nbits| { | ||
| 1404 | if (bits <= nbits) { | ||
| 1405 | try w.print("{c}{d}", .{ prefix_byte, nbits }); | ||
| 1406 | break; | ||
| 1407 | } | ||
| 1408 | } else { | ||
| 1409 | unreachable; | ||
| 1410 | } | ||
| 1411 | }, | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | try w.writeByte('('); | ||
| 1415 | try o.writeCValue(w, lhs); | ||
| 1416 | try w.writeAll(", "); | ||
| 1417 | try o.writeCValue(w, rhs); | ||
| 1418 | |||
| 1419 | if (int_info.signedness == .signed) { | ||
| 1420 | try w.print(", {s}", .{min}); | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | try w.print(", {s});", .{max}); | ||
| 1424 | try o.indent_writer.insertNewline(); | ||
| 1425 | |||
| 1426 | return ret; | ||
| 1427 | } | ||
| 1428 | |||
| 1315 | fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { | 1429 | fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1316 | if (f.liveness.isUnused(inst)) | 1430 | if (f.liveness.isUnused(inst)) |
| 1317 | return CValue.none; | 1431 | return CValue.none; |
src/codegen/llvm.zig+3-3| ... | @@ -2038,7 +2038,7 @@ pub const FuncGen = struct { | ... | @@ -2038,7 +2038,7 @@ pub const FuncGen = struct { |
| 2038 | const rhs = try self.resolveInst(bin_op.rhs); | 2038 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2039 | const inst_ty = self.air.typeOfIndex(inst); | 2039 | const inst_ty = self.air.typeOfIndex(inst); |
| 2040 | 2040 | ||
| 2041 | if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, ""); | 2041 | if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, ""); |
| 2042 | if (ty == .wrapping) | 2042 | if (ty == .wrapping) |
| 2043 | return self.builder.buildAdd(lhs, rhs, "") | 2043 | return self.builder.buildAdd(lhs, rhs, "") |
| 2044 | else if (ty == .saturated) { | 2044 | else if (ty == .saturated) { |
| ... | @@ -2060,7 +2060,7 @@ pub const FuncGen = struct { | ... | @@ -2060,7 +2060,7 @@ pub const FuncGen = struct { |
| 2060 | const rhs = try self.resolveInst(bin_op.rhs); | 2060 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2061 | const inst_ty = self.air.typeOfIndex(inst); | 2061 | const inst_ty = self.air.typeOfIndex(inst); |
| 2062 | 2062 | ||
| 2063 | if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, ""); | 2063 | if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, ""); |
| 2064 | if (ty == .wrapping) | 2064 | if (ty == .wrapping) |
| 2065 | return self.builder.buildSub(lhs, rhs, "") | 2065 | return self.builder.buildSub(lhs, rhs, "") |
| 2066 | else if (ty == .saturated) { | 2066 | else if (ty == .saturated) { |
| ... | @@ -2082,7 +2082,7 @@ pub const FuncGen = struct { | ... | @@ -2082,7 +2082,7 @@ pub const FuncGen = struct { |
| 2082 | const rhs = try self.resolveInst(bin_op.rhs); | 2082 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2083 | const inst_ty = self.air.typeOfIndex(inst); | 2083 | const inst_ty = self.air.typeOfIndex(inst); |
| 2084 | 2084 | ||
| 2085 | if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, ""); | 2085 | if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, ""); |
| 2086 | if (ty == .wrapping) | 2086 | if (ty == .wrapping) |
| 2087 | return self.builder.buildMul(lhs, rhs, "") | 2087 | return self.builder.buildMul(lhs, rhs, "") |
| 2088 | else if (ty == .saturated) { | 2088 | else if (ty == .saturated) { |
src/link/C/zig.h+93| ... | @@ -356,3 +356,96 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon | ... | @@ -356,3 +356,96 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon |
| 356 | return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs)); | 356 | return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs)); |
| 357 | } | 357 | } |
| 358 | 358 | ||
| 359 | /* | ||
| 360 | * Saturating aritmetic operations: add, sub, mul, shl | ||
| 361 | */ | ||
| 362 | #define zig_add_sat_u(ZT, T) static inline T zig_adds_##ZT(T x, T y, T max) { \ | ||
| 363 | return (x > max - y) ? max : x + y; \ | ||
| 364 | } | ||
| 365 | |||
| 366 | #define zig_add_sat_s(ZT, T, T2) static inline T zig_adds_##ZT(T2 x, T2 y, T2 min, T2 max) { \ | ||
| 367 | T2 res = x + y; \ | ||
| 368 | return (res < min) ? min : (res > max) ? max : res; \ | ||
| 369 | } | ||
| 370 | |||
| 371 | zig_add_sat_u( u8, uint8_t) | ||
| 372 | zig_add_sat_s( i8, int8_t, int16_t) | ||
| 373 | zig_add_sat_u(u16, uint16_t) | ||
| 374 | zig_add_sat_s(i16, int16_t, int32_t) | ||
| 375 | zig_add_sat_u(u32, uint32_t) | ||
| 376 | zig_add_sat_s(i32, int32_t, int64_t) | ||
| 377 | zig_add_sat_u(u64, uint64_t) | ||
| 378 | zig_add_sat_s(i64, int64_t, int128_t) | ||
| 379 | zig_add_sat_s(isize, intptr_t, int128_t) | ||
| 380 | zig_add_sat_s(short, short, int) | ||
| 381 | zig_add_sat_s(int, int, long) | ||
| 382 | zig_add_sat_s(long, long, long long) | ||
| 383 | |||
| 384 | #define zig_sub_sat_u(ZT, T) static inline T zig_subs_##ZT(T x, T y, T max) { \ | ||
| 385 | return (x > max + y) ? max : x - y; \ | ||
| 386 | } | ||
| 387 | |||
| 388 | #define zig_sub_sat_s(ZT, T, T2) static inline T zig_subs_##ZT(T2 x, T2 y, T2 min, T2 max) { \ | ||
| 389 | T2 res = x - y; \ | ||
| 390 | return (res < min) ? min : (res > max) ? max : res; \ | ||
| 391 | } | ||
| 392 | |||
| 393 | zig_sub_sat_u( u8, uint8_t) | ||
| 394 | zig_sub_sat_s( i8, int8_t, int16_t) | ||
| 395 | zig_sub_sat_u(u16, uint16_t) | ||
| 396 | zig_sub_sat_s(i16, int16_t, int32_t) | ||
| 397 | zig_sub_sat_u(u32, uint32_t) | ||
| 398 | zig_sub_sat_s(i32, int32_t, int64_t) | ||
| 399 | zig_sub_sat_u(u64, uint64_t) | ||
| 400 | zig_sub_sat_s(i64, int64_t, int128_t) | ||
| 401 | zig_sub_sat_s(isize, intptr_t, int128_t) | ||
| 402 | zig_sub_sat_s(short, short, int) | ||
| 403 | zig_sub_sat_s(int, int, long) | ||
| 404 | zig_sub_sat_s(long, long, long long) | ||
| 405 | |||
| 406 | |||
| 407 | #define zig_mul_sat_u(ZT, T, T2) static inline T zig_muls_##ZT(T2 x, T2 y, T2 max) { \ | ||
| 408 | T2 res = x * y; \ | ||
| 409 | return (res > max) ? max : res; \ | ||
| 410 | } | ||
| 411 | |||
| 412 | #define zig_mul_sat_s(ZT, T, T2) static inline T zig_muls_##ZT(T2 x, T2 y, T2 min, T2 max) { \ | ||
| 413 | T2 res = x * y; \ | ||
| 414 | return (res < min) ? min : (res > max) ? max : res; \ | ||
| 415 | } | ||
| 416 | |||
| 417 | zig_mul_sat_u(u8, uint8_t, uint16_t) | ||
| 418 | zig_mul_sat_s(i8, int8_t, int16_t) | ||
| 419 | zig_mul_sat_u(u16, uint16_t, uint32_t) | ||
| 420 | zig_mul_sat_s(i16, int16_t, int32_t) | ||
| 421 | zig_mul_sat_u(u32, uint32_t, uint64_t) | ||
| 422 | zig_mul_sat_s(i32, int32_t, int64_t) | ||
| 423 | zig_mul_sat_u(u64, uint64_t, uint128_t) | ||
| 424 | zig_mul_sat_s(i64, int64_t, int128_t) | ||
| 425 | zig_mul_sat_s(isize, intptr_t, int128_t) | ||
| 426 | zig_mul_sat_s(short, short, int) | ||
| 427 | zig_mul_sat_s(int, int, long) | ||
| 428 | zig_mul_sat_s(long, long, long long) | ||
| 429 | |||
| 430 | #define zig_shl_sat_u(ZT, T, bits) static inline T zig_shls_##ZT(T x, T y, T max) { \ | ||
| 431 | T leading_zeros = __builtin_clz(x); \ | ||
| 432 | return (leading_zeros + y > bits) ? max : x << y; \ | ||
| 433 | } | ||
| 434 | |||
| 435 | #define zig_shl_sat_s(ZT, T, bits) static inline T zig_shls_##ZT(T x, T y, T min, T max) { \ | ||
| 436 | T leading_zeros = __builtin_clz(x & ~max); \ | ||
| 437 | return (leading_zeros + y > bits) ? max : x << y; \ | ||
| 438 | } | ||
| 439 | |||
| 440 | zig_shl_sat_u(u8, uint8_t, 8) | ||
| 441 | zig_shl_sat_s(i8, int8_t, 7) | ||
| 442 | zig_shl_sat_u(u16, uint16_t, 16) | ||
| 443 | zig_shl_sat_s(i16, int16_t, 15) | ||
| 444 | zig_shl_sat_u(u32, uint32_t, 32) | ||
| 445 | zig_shl_sat_s(i32, int32_t, 31) | ||
| 446 | zig_shl_sat_u(u64, uint64_t, 64) | ||
| 447 | zig_shl_sat_s(i64, int64_t, 63) | ||
| 448 | zig_shl_sat_s(isize, intptr_t, 63) | ||
| 449 | zig_shl_sat_s(short, short, 15) | ||
| 450 | zig_shl_sat_s(int, int, 31) | ||
| 451 | zig_shl_sat_s(long, long, 63) | ||
| \ No newline at end of file | |||