Carry slice element pointers through loops
8c7552bb010a226d2686724e93e8e5d064ec01cdf46523185eebb9937588f7e4
1 parent
5573da2c
lib/std/lang/gen/regalloc/assign.rad
+1 -2
| 229 | 229 | return nil; |
|
| 230 | 230 | } |
|
| 231 | 231 | ||
| 232 | 232 | /// Find first free register in pool, allocate it, return it. |
|
| 233 | 233 | unsafe fn findFreeInPool(usedRegs: &mut bitset::Bitset, current: &mut RegMap, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg { |
|
| 234 | - | for i in 0..pool.len { |
|
| 235 | - | let r = pool[i]; |
|
| 234 | + | for r in pool { |
|
| 236 | 235 | if not bitset::contains(usedRegs, *r as u32) { |
|
| 237 | 236 | bitset::put(usedRegs, *r as u32); |
|
| 238 | 237 | rmapSet(current, ssaReg, r); |
|
| 239 | 238 | return r; |
|
| 240 | 239 | } |
lib/std/lang/gen/regalloc/liveness.rad
+4 -5
| 82 | 82 | for b in 0..blockCount { |
|
| 83 | 83 | let block = &func.blocks[b]; |
|
| 84 | 84 | for p in block.params { |
|
| 85 | 85 | countRegisters(p.value, &mut registerCount); |
|
| 86 | 86 | } |
|
| 87 | - | for i in 0..block.instrs.len { |
|
| 88 | - | il::forEachReg(block.instrs[i], countRegistersCallback, &mut registerCount as &mut opaque); |
|
| 89 | - | if let dst = il::instrDst(block.instrs[i]) { |
|
| 87 | + | for instr in block.instrs { |
|
| 88 | + | il::forEachReg(instr, countRegistersCallback, &mut registerCount as &mut opaque); |
|
| 89 | + | if let dst = il::instrDst(instr) { |
|
| 90 | 90 | countRegisters(dst, &mut registerCount); |
|
| 91 | 91 | } |
|
| 92 | 92 | } |
|
| 93 | 93 | } |
|
| 94 | 94 | if registerCount > MAX_SSA_REGS as u64 { |
| 158 | 158 | /// Compute local defs and uses for a single block. |
|
| 159 | 159 | unsafe fn computeLocalDefsUses(block: *unsafe il::Block, defs: *unsafe mut bitset::Bitset, uses: *unsafe mut bitset::Bitset) { |
|
| 160 | 160 | for p in block.params { |
|
| 161 | 161 | bitset::put(defs, p.value.n); |
|
| 162 | 162 | } |
|
| 163 | - | for i in 0..block.instrs.len { |
|
| 164 | - | let instr = block.instrs[i]; |
|
| 163 | + | for instr in block.instrs { |
|
| 165 | 164 | let mut ctx = DefsUses { defs, uses }; |
|
| 166 | 165 | il::forEachReg(instr, addUseCallback, &mut ctx as &mut opaque); |
|
| 167 | 166 | ||
| 168 | 167 | if let dst = il::instrDst(instr) { |
|
| 169 | 168 | bitset::put(defs, dst.n); |
lib/std/lang/lower.rad
+125 -25
| 560 | 560 | export record LoopCtx: Copy { |
|
| 561 | 561 | /// Where `break` should transfer control (the loop's exit block). |
|
| 562 | 562 | breakTarget: BlockId, |
|
| 563 | 563 | /// Where `continue` should transfer control. |
|
| 564 | 564 | continueTarget: ?BlockId, |
|
| 565 | + | /// Checked slice whose exact loop index selects the current element. |
|
| 566 | + | indexedSlice: ?IndexedSliceLoop, |
|
| 567 | + | } |
|
| 568 | + | ||
| 569 | + | /// Loop state for a checked slice indexed by its length-bounded range. |
|
| 570 | + | record IndexedSliceLoop: Copy { |
|
| 571 | + | /// Slice binding resolved for the range bound. |
|
| 572 | + | sourceSym: *unsafe mut resolver::Symbol, |
|
| 573 | + | /// Range index binding. |
|
| 574 | + | indexSym: *unsafe mut resolver::Symbol, |
|
| 575 | + | /// Address of the current element. |
|
| 576 | + | ptrVar: Var, |
|
| 577 | + | /// Element type used by the subscript. |
|
| 578 | + | item: *resolver::Type, |
|
| 565 | 579 | } |
|
| 566 | 580 | ||
| 567 | 581 | /// Logical operator. |
|
| 568 | 582 | union LogicalOp: Copy { And, Or } |
|
| 569 | 583 |
| 574 | 588 | valVar: Var, |
|
| 575 | 589 | indexVar: ?Var, |
|
| 576 | 590 | endVal: il::Val, |
|
| 577 | 591 | valType: il::Type, |
|
| 578 | 592 | unsigned: bool, |
|
| 593 | + | indexedSlice: ?IndexedSliceLoop, |
|
| 579 | 594 | }, |
|
| 580 | 595 | /// Collection iterator: `for elem in slice`. |
|
| 581 | 596 | Collection { |
|
| 582 | 597 | valVar: ?Var, |
|
| 583 | - | idxVar: Var, |
|
| 584 | - | dataReg: il::Reg, |
|
| 585 | - | lengthVal: il::Val, |
|
| 598 | + | bound: CollectionBound, |
|
| 586 | 599 | elemType: *resolver::Type, |
|
| 587 | 600 | }, |
|
| 588 | 601 | } |
|
| 589 | 602 | ||
| 603 | + | /// State used to bound collection iteration. |
|
| 604 | + | union CollectionBound: Copy { |
|
| 605 | + | /// An index bounds loops that expose it or have no element pointer. |
|
| 606 | + | Index { idxVar: Var, ptrVar: ?Var, lengthVal: il::Val }, |
|
| 607 | + | /// The element address bounds loops without an index binding. |
|
| 608 | + | Pointer { ptrVar: Var, endVal: il::Val }, |
|
| 609 | + | } |
|
| 610 | + | ||
| 590 | 611 | ////////////////////////////// |
|
| 591 | 612 | // Pattern Matching Support // |
|
| 592 | 613 | ////////////////////////////// |
|
| 593 | 614 | ||
| 594 | 615 | // Match expressions are lowered by evaluating the subject once, then emitting |
| 2678 | 2699 | assert self.loopDepth < self.loopStack.len, "enterLoop: loop depth overflow"; |
|
| 2679 | 2700 | let slot = &mut self.loopStack[self.loopDepth]; |
|
| 2680 | 2701 | ||
| 2681 | 2702 | set slot.breakTarget = breakBlock; |
|
| 2682 | 2703 | set slot.continueTarget = continueBlock; |
|
| 2704 | + | set slot.indexedSlice = nil; |
|
| 2683 | 2705 | set self.loopDepth += 1; |
|
| 2684 | 2706 | } |
|
| 2685 | 2707 | ||
| 2686 | 2708 | /// Exit the current loop context. |
|
| 2687 | 2709 | fn exitLoop(self: &mut FnLowerer) { |
| 5176 | 5198 | /// the data pointer (for slices), and emitting an [`il::Instr::Elem`] to compute |
|
| 5177 | 5199 | /// the element address. |
|
| 5178 | 5200 | unsafe fn lowerElemPtr( |
|
| 5179 | 5201 | self: &mut FnLowerer, container: *ast::Node, index: *ast::Node |
|
| 5180 | 5202 | ) -> ElemPtrResult throws (LowerError) { |
|
| 5203 | + | if let case ast::NodeValue::Ident(_) = container.value { |
|
| 5204 | + | if let case ast::NodeValue::Ident(_) = index.value { |
|
| 5205 | + | let sourceSym = resolver::symbolFor(self.low.resolver, container); |
|
| 5206 | + | let indexSym = resolver::symbolFor(self.low.resolver, index); |
|
| 5207 | + | for depth in 0..self.loopDepth { |
|
| 5208 | + | let indexed = self.loopStack[depth].indexedSlice else continue; |
|
| 5209 | + | if sourceSym == indexed.sourceSym and indexSym == indexed.indexSym { |
|
| 5210 | + | let ptr = emitValToReg(self, try useVar(self, indexed.ptrVar)); |
|
| 5211 | + | return ElemPtrResult { elemReg: ptr, elemType: *indexed.item }; |
|
| 5212 | + | } |
|
| 5213 | + | } |
|
| 5214 | + | } |
|
| 5215 | + | } |
|
| 5181 | 5216 | let containerTy = try typeOf(self, container); |
|
| 5182 | 5217 | let subjectTy = resolver::autoDeref(containerTy); |
|
| 5183 | 5218 | let baseVal = try lowerExpr(self, container); |
|
| 5184 | 5219 | let indexVal = try lowerExpr(self, index); |
|
| 5185 | 5220 | let baseReg = emitValToReg(self, baseVal); |
| 5613 | 5648 | ||
| 5614 | 5649 | try switchToAndSeal(self, endBlock); |
|
| 5615 | 5650 | exitLoop(self); |
|
| 5616 | 5651 | } |
|
| 5617 | 5652 | ||
| 5618 | - | /// Emit an increment of a variable by `1`. |
|
| 5619 | - | unsafe fn emitIncrement(self: &mut FnLowerer, v: Var, typ: il::Type) throws (LowerError) { |
|
| 5653 | + | /// Emit an increment of a variable by a fixed step. |
|
| 5654 | + | unsafe fn emitIncrement(self: &mut FnLowerer, v: Var, typ: il::Type, step: u32) throws (LowerError) { |
|
| 5620 | 5655 | let cur = try useVar(self, v); |
|
| 5621 | - | let next = nextReg(self); |
|
| 5622 | - | ||
| 5623 | - | emit(self, il::Instr::BinOp { op: il::BinOp::Add, typ, dst: next, a: cur, b: il::Val::Imm(1) }); |
|
| 5624 | - | defVar(self, v, il::Val::Reg(next)); |
|
| 5656 | + | let next = emitTypedBinOp(self, il::BinOp::Add, typ, cur, il::Val::Imm(step as i64)); |
|
| 5657 | + | defVar(self, v, next); |
|
| 5625 | 5658 | } |
|
| 5626 | 5659 | ||
| 5627 | 5660 | /// Common for-loop lowering for both range and collection iterators. |
|
| 5628 | 5661 | /// |
|
| 5629 | 5662 | /// The step block is created lazily (after the loop body) so that it gets |
| 5635 | 5668 | let loopBlock = try createBlock(self, "loop"); |
|
| 5636 | 5669 | let bodyBlock = try createBlock(self, "body"); |
|
| 5637 | 5670 | let endBlock = try createBlock(self, "merge"); |
|
| 5638 | 5671 | ||
| 5639 | 5672 | enterLoop(self, endBlock, nil); |
|
| 5673 | + | if let case ForIter::Range { indexedSlice, .. } = *iter { |
|
| 5674 | + | let ctx = currentLoop(self) else panic "lowerForLoop: missing loop context"; |
|
| 5675 | + | set ctx.indexedSlice = indexedSlice; |
|
| 5676 | + | } |
|
| 5640 | 5677 | try switchAndJumpTo(self, loopBlock); |
|
| 5641 | 5678 | ||
| 5642 | 5679 | // Emit condition check. |
|
| 5643 | 5680 | match *iter { |
|
| 5644 | 5681 | case ForIter::Range { valVar, endVal, valType, unsigned, .. } => { |
|
| 5645 | 5682 | let curVal = try useVar(self, valVar); |
|
| 5646 | 5683 | let cmp = il::CmpOp::Ult if unsigned else il::CmpOp::Slt; |
|
| 5647 | 5684 | try emitBrCmp(self, cmp, valType, curVal, endVal, bodyBlock, endBlock); |
|
| 5648 | 5685 | } |
|
| 5649 | - | case ForIter::Collection { idxVar, lengthVal, .. } => { |
|
| 5650 | - | let curIdx = try useVar(self, idxVar); |
|
| 5651 | - | try emitBrCmp(self, il::CmpOp::Slt, il::Type::W32, curIdx, lengthVal, bodyBlock, endBlock); |
|
| 5686 | + | case ForIter::Collection { bound, .. } => match bound { |
|
| 5687 | + | case CollectionBound::Index { idxVar, lengthVal, .. } => { |
|
| 5688 | + | let curIdx = try useVar(self, idxVar); |
|
| 5689 | + | try emitBrCmp(self, il::CmpOp::Slt, il::Type::W32, curIdx, lengthVal, bodyBlock, endBlock); |
|
| 5690 | + | }, |
|
| 5691 | + | case CollectionBound::Pointer { ptrVar, endVal } => { |
|
| 5692 | + | let curPtr = try useVar(self, ptrVar); |
|
| 5693 | + | try emitBrCmp(self, il::CmpOp::Ne, il::Type::W64, curPtr, endVal, bodyBlock, endBlock); |
|
| 5694 | + | }, |
|
| 5652 | 5695 | } |
|
| 5653 | 5696 | } |
|
| 5654 | 5697 | // Switch to loop body. |
|
| 5655 | 5698 | try switchToAndSeal(self, bodyBlock); |
|
| 5656 | 5699 | ||
| 5657 | 5700 | // Emit element binding, only for collections. |
|
| 5658 | 5701 | // Reads the element at the current index. |
|
| 5659 | - | if let case ForIter::Collection { valVar, idxVar, dataReg, elemType, .. } = *iter { |
|
| 5702 | + | let mut elementPtr: ?Var = nil; |
|
| 5703 | + | if let case ForIter::Collection { valVar, bound, elemType } = *iter { |
|
| 5704 | + | match bound { |
|
| 5705 | + | case CollectionBound::Index { ptrVar, .. } => set elementPtr = ptrVar, |
|
| 5706 | + | case CollectionBound::Pointer { ptrVar, .. } => set elementPtr = ptrVar, |
|
| 5707 | + | } |
|
| 5660 | 5708 | if let v = valVar { |
|
| 5661 | - | let curIdx = try useVar(self, idxVar); |
|
| 5662 | - | let elemReg = emitElem(self, resolver::getTypeLayout(*elemType).size, dataReg, curIdx); |
|
| 5709 | + | let ptr = elementPtr else panic "lowerForLoop: missing collection pointer"; |
|
| 5710 | + | let elemReg = emitValToReg(self, try useVar(self, ptr)); |
|
| 5663 | 5711 | let val = emitRead(self, elemReg, 0, *elemType); |
|
| 5664 | 5712 | ||
| 5665 | 5713 | defVar(self, v, val); |
|
| 5666 | 5714 | } |
|
| 5667 | 5715 | } |
| 5677 | 5725 | } |
|
| 5678 | 5726 | // Otherwise, emit increment directly in the current block, |
|
| 5679 | 5727 | // saving the jump to a separate step block. |
|
| 5680 | 5728 | if not blockHasTerminator(self) { |
|
| 5681 | 5729 | match *iter { |
|
| 5682 | - | case ForIter::Range { valVar, valType, indexVar, .. } => { |
|
| 5683 | - | try emitIncrement(self, valVar, valType); |
|
| 5730 | + | case ForIter::Range { valVar, valType, indexVar, indexedSlice, .. } => { |
|
| 5731 | + | try emitIncrement(self, valVar, valType, 1); |
|
| 5684 | 5732 | if let idxVar = indexVar { |
|
| 5685 | - | try emitIncrement(self, idxVar, il::Type::W32); |
|
| 5733 | + | try emitIncrement(self, idxVar, il::Type::W32, 1); |
|
| 5734 | + | } |
|
| 5735 | + | if let indexed = indexedSlice { |
|
| 5736 | + | let stride = resolver::getTypeLayout(*indexed.item).size; |
|
| 5737 | + | try emitIncrement(self, indexed.ptrVar, il::Type::W64, stride); |
|
| 5686 | 5738 | } |
|
| 5687 | 5739 | } |
|
| 5688 | - | case ForIter::Collection { idxVar, .. } => { |
|
| 5689 | - | try emitIncrement(self, idxVar, il::Type::W32); |
|
| 5740 | + | case ForIter::Collection { bound, elemType, .. } => { |
|
| 5741 | + | if let case CollectionBound::Index { idxVar, .. } = bound { |
|
| 5742 | + | try emitIncrement(self, idxVar, il::Type::W32, 1); |
|
| 5743 | + | } |
|
| 5744 | + | if let ptr = elementPtr { |
|
| 5745 | + | let stride = resolver::getTypeLayout(*elemType).size; |
|
| 5746 | + | try emitIncrement(self, ptr, il::Type::W64, stride); |
|
| 5747 | + | } |
|
| 5690 | 5748 | } |
|
| 5691 | 5749 | } |
|
| 5692 | 5750 | try emitJmp(self, loopBlock); |
|
| 5693 | 5751 | } |
|
| 5694 | 5752 | exitLoop(self); |
| 5704 | 5762 | let savedVarsLen = enterVarScope(self); |
|
| 5705 | 5763 | let info = resolver::forLoopInfoFor(self.low.resolver, node) else { |
|
| 5706 | 5764 | throw LowerError::MissingMetadata; |
|
| 5707 | 5765 | }; |
|
| 5708 | 5766 | match info { |
|
| 5709 | - | case resolver::ForLoopInfo::Range { valType, range, bindingName, indexName } => { |
|
| 5767 | + | case resolver::ForLoopInfo::Range { valType, range, bindingName, indexName, indexedSlice } => { |
|
| 5710 | 5768 | let endExpr = range.end else { |
|
| 5711 | 5769 | throw LowerError::MissingMetadata; |
|
| 5712 | 5770 | }; |
|
| 5713 | 5771 | let mut startVal = il::Val::Imm(0); |
|
| 5714 | 5772 | if let start = range.start { |
| 5720 | 5778 | ||
| 5721 | 5779 | let mut indexVar: ?Var = nil; |
|
| 5722 | 5780 | if indexName <> nil { // Optional index always starts at zero. |
|
| 5723 | 5781 | set indexVar = newVar(self, indexName, il::Type::W32, false, il::Val::Imm(0)); |
|
| 5724 | 5782 | } |
|
| 5783 | + | let mut indexedLoop: ?IndexedSliceLoop = nil; |
|
| 5784 | + | if let info = indexedSlice { |
|
| 5785 | + | let sourceSym = resolver::symbolFor(self.low.resolver, info.source) else { |
|
| 5786 | + | throw LowerError::MissingMetadata; |
|
| 5787 | + | }; |
|
| 5788 | + | let indexSym = resolver::symbolFor(self.low.resolver, f.binding) else { |
|
| 5789 | + | throw LowerError::MissingMetadata; |
|
| 5790 | + | }; |
|
| 5791 | + | let sliceVal = try lowerExpr(self, info.source); |
|
| 5792 | + | let sliceReg = emitValToReg(self, sliceVal); |
|
| 5793 | + | let dataReg = loadSlicePtr(self, sliceReg); |
|
| 5794 | + | let ptrVar = newVar(self, nil, il::Type::W64, false, il::Val::Reg(dataReg)); |
|
| 5795 | + | set indexedLoop = IndexedSliceLoop { |
|
| 5796 | + | sourceSym, indexSym, ptrVar, item: info.item, |
|
| 5797 | + | }; |
|
| 5798 | + | } |
|
| 5725 | 5799 | let iter = ForIter::Range { |
|
| 5726 | 5800 | valVar, indexVar, endVal, valType: iterType, |
|
| 5727 | 5801 | unsigned: isUnsignedType(*valType), |
|
| 5802 | + | indexedSlice: indexedLoop, |
|
| 5728 | 5803 | }; |
|
| 5729 | 5804 | ||
| 5730 | 5805 | try lowerForLoop(self, &iter, f.body); |
|
| 5731 | 5806 | } |
|
| 5732 | 5807 | case resolver::ForLoopInfo::Collection { elemType, length, bindingName, indexName } => { |
| 5739 | 5814 | set lengthVal = il::Val::Imm(len as i64); |
|
| 5740 | 5815 | } else { // Slice (length must be loaded). |
|
| 5741 | 5816 | set lengthVal = loadSliceLen(self, containerReg); |
|
| 5742 | 5817 | set dataReg = loadSlicePtr(self, containerReg); |
|
| 5743 | 5818 | } |
|
| 5744 | - | // Declare index value binidng. |
|
| 5745 | - | let idxVar = newVar(self, indexName, il::Type::W32, false, il::Val::Imm(0)); |
|
| 5746 | - | ||
| 5747 | 5819 | // Declare element value binding. |
|
| 5820 | + | let mut ptrVar: ?Var = nil; |
|
| 5748 | 5821 | let mut valVar: ?Var = nil; |
|
| 5749 | 5822 | if bindingName <> nil { |
|
| 5823 | + | set ptrVar = newVar(self, nil, il::Type::W64, false, il::Val::Reg(dataReg)); |
|
| 5750 | 5824 | set valVar = newVar( |
|
| 5751 | 5825 | self, |
|
| 5752 | 5826 | bindingName, |
|
| 5753 | 5827 | ilType(self.low, *elemType), |
|
| 5754 | 5828 | false, |
|
| 5755 | 5829 | il::Val::Undef |
|
| 5756 | 5830 | ); |
|
| 5757 | 5831 | } |
|
| 5758 | - | let iter = ForIter::Collection { valVar, idxVar, dataReg, lengthVal, elemType }; |
|
| 5832 | + | let stride = resolver::getTypeLayout(*elemType).size; |
|
| 5833 | + | let mut bound: CollectionBound = undefined; |
|
| 5834 | + | // A nonzero u32 stride reaches the end address after exactly |
|
| 5835 | + | // length steps because length times stride fits in u64. |
|
| 5836 | + | if indexName == nil and stride > 0 and ptrVar <> nil { |
|
| 5837 | + | let mut endVal: il::Val = undefined; |
|
| 5838 | + | if let len = length { |
|
| 5839 | + | let bytes = len as u64 * stride as u64; |
|
| 5840 | + | set endVal = emitTypedBinOp( |
|
| 5841 | + | self, il::BinOp::Add, il::Type::W64, |
|
| 5842 | + | il::Val::Reg(dataReg), il::Val::Imm(bytes as i64) |
|
| 5843 | + | ); |
|
| 5844 | + | } else { |
|
| 5845 | + | let dst = nextReg(self); |
|
| 5846 | + | emit(self, il::Instr::Zext { typ: il::Type::W32, dst, val: lengthVal }); |
|
| 5847 | + | set endVal = il::Val::Reg(emitElem(self, stride, dataReg, il::Val::Reg(dst))); |
|
| 5848 | + | } |
|
| 5849 | + | let ptr = ptrVar else panic "lowerFor: missing collection pointer"; |
|
| 5850 | + | set bound = CollectionBound::Pointer { |
|
| 5851 | + | ptrVar: ptr, |
|
| 5852 | + | endVal, |
|
| 5853 | + | }; |
|
| 5854 | + | } else { |
|
| 5855 | + | let idxVar = newVar(self, indexName, il::Type::W32, false, il::Val::Imm(0)); |
|
| 5856 | + | set bound = CollectionBound::Index { idxVar, ptrVar, lengthVal }; |
|
| 5857 | + | } |
|
| 5858 | + | let iter = ForIter::Collection { valVar, bound, elemType }; |
|
| 5759 | 5859 | ||
| 5760 | 5860 | try lowerForLoop(self, &iter, f.body); |
|
| 5761 | 5861 | } |
|
| 5762 | 5862 | } |
|
| 5763 | 5863 | exitVarScope(self, savedVarsLen); |
lib/std/lang/resolver.rad
+58 -5
| 253 | 253 | mutable: bool, |
|
| 254 | 254 | /// Static capacity if container is an array. |
|
| 255 | 255 | capacity: ?u32, |
|
| 256 | 256 | } |
|
| 257 | 257 | ||
| 258 | + | /// Checked slice whose length bounds an indexed range loop. |
|
| 259 | + | export record IndexedSliceInfo: Copy { |
|
| 260 | + | /// Identifier that provides the slice header. |
|
| 261 | + | source: *ast::Node, |
|
| 262 | + | /// Type of each slice element. |
|
| 263 | + | item: *Type, |
|
| 264 | + | } |
|
| 265 | + | ||
| 258 | 266 | /// Pre-computed metadata for `for` loop iteration. |
|
| 259 | 267 | /// Used by the lowerer to avoid re-analyzing the iterable type. |
|
| 260 | 268 | export union ForLoopInfo: Copy { |
|
| 261 | 269 | /// Iterating over a range expression (e.g., `for i in 0..n`). |
|
| 262 | 270 | Range { |
|
| 263 | 271 | valType: *Type, |
|
| 264 | 272 | range: ast::Range, |
|
| 265 | 273 | bindingName: ?*[u8], |
|
| 266 | - | indexName: ?*[u8] |
|
| 274 | + | indexName: ?*[u8], |
|
| 275 | + | indexedSlice: ?IndexedSliceInfo, |
|
| 267 | 276 | }, |
|
| 268 | 277 | /// Iterating over an array or slice. For arrays, the length field is set. |
|
| 269 | 278 | Collection { |
|
| 270 | 279 | elemType: *Type, |
|
| 271 | 280 | length: ?u32, |
| 4698 | 4707 | try visitOptional(self, loopNode.elseBranch, Type::Void); |
|
| 4699 | 4708 | ||
| 4700 | 4709 | return setNodeType(self, node, Type::Void); |
|
| 4701 | 4710 | } |
|
| 4702 | 4711 | ||
| 4712 | + | /// Find a stable checked slice whose length bounds a range index. |
|
| 4713 | + | unsafe fn indexedSliceForRange(self: &Resolver, binding: *ast::Node, range: ast::Range) -> ?IndexedSliceInfo { |
|
| 4714 | + | let case ast::NodeValue::Ident(_) = binding.value else { |
|
| 4715 | + | return nil; |
|
| 4716 | + | }; |
|
| 4717 | + | let start = range.start else { |
|
| 4718 | + | return nil; |
|
| 4719 | + | }; |
|
| 4720 | + | let case ast::NodeValue::Number(zero) = start.value else { |
|
| 4721 | + | return nil; |
|
| 4722 | + | }; |
|
| 4723 | + | if zero.magnitude <> 0 { |
|
| 4724 | + | return nil; |
|
| 4725 | + | } |
|
| 4726 | + | let end = range.end else { |
|
| 4727 | + | return nil; |
|
| 4728 | + | }; |
|
| 4729 | + | let case ast::NodeValue::FieldAccess(access) = end.value else { |
|
| 4730 | + | return nil; |
|
| 4731 | + | }; |
|
| 4732 | + | let case ast::NodeValue::Ident(field) = access.child.value else { |
|
| 4733 | + | return nil; |
|
| 4734 | + | }; |
|
| 4735 | + | if not mem::eq(field, LEN_FIELD) { |
|
| 4736 | + | return nil; |
|
| 4737 | + | } |
|
| 4738 | + | let case ast::NodeValue::Ident(_) = access.parent.value else { |
|
| 4739 | + | return nil; |
|
| 4740 | + | }; |
|
| 4741 | + | let source = access.parent; |
|
| 4742 | + | let sym = symbolFor(self, source) else { |
|
| 4743 | + | return nil; |
|
| 4744 | + | }; |
|
| 4745 | + | let case SymbolData::Value { mutable: false, type: ty, .. } = sym.data else { |
|
| 4746 | + | return nil; |
|
| 4747 | + | }; |
|
| 4748 | + | let case Type::Slice { class: types::PointerClass::Ref, item, mutable: false } = ty else { |
|
| 4749 | + | return nil; |
|
| 4750 | + | }; |
|
| 4751 | + | return IndexedSliceInfo { source, item }; |
|
| 4752 | + | } |
|
| 4753 | + | ||
| 4703 | 4754 | /// Analyze a `for` loop, binding iteration variables. |
|
| 4704 | 4755 | unsafe fn resolveFor(self: &mut Resolver, node: *ast::Node, forStmt: ast::For) -> Type |
|
| 4705 | 4756 | throws (ResolveError) |
|
| 4706 | 4757 | { |
|
| 4707 | 4758 | let iterableTy = try infer(self, forStmt.iterable); |
| 4717 | 4768 | set indexName = name; |
|
| 4718 | 4769 | } |
|
| 4719 | 4770 | } |
|
| 4720 | 4771 | // Extract item type and store pre-computed loop metadata for the lowerer. |
|
| 4721 | 4772 | let mut itemTy: Type = undefined; |
|
| 4773 | + | let mut hasElementPointer = bindingName <> nil; |
|
| 4722 | 4774 | match iterableTy { |
|
| 4723 | 4775 | case Type::Slice { item, class, .. } => { |
|
| 4724 | 4776 | if class == types::PointerClass::Unsafe { |
|
| 4725 | 4777 | try requireUnsafe(self, forStmt.iterable); |
|
| 4726 | 4778 | } |
| 4738 | 4790 | let case ast::NodeValue::Range(range) = forStmt.iterable.value else { |
|
| 4739 | 4791 | throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable); |
|
| 4740 | 4792 | }; |
|
| 4741 | 4793 | set itemTy = *valType; |
|
| 4742 | 4794 | ||
| 4795 | + | let indexedSlice = indexedSliceForRange(self, forStmt.binding, range); |
|
| 4743 | 4796 | setForLoopInfo(self, node, ForLoopInfo::Range { |
|
| 4744 | - | valType, range, bindingName, indexName |
|
| 4797 | + | valType, range, bindingName, indexName, indexedSlice |
|
| 4745 | 4798 | }); |
|
| 4799 | + | set hasElementPointer = indexedSlice <> nil; |
|
| 4746 | 4800 | } |
|
| 4747 | 4801 | case Type::Array(arrayInfo) => { |
|
| 4748 | 4802 | set itemTy = *arrayInfo.item; |
|
| 4749 | 4803 | setForLoopInfo(self, node, ForLoopInfo::Collection { |
|
| 4750 | 4804 | elemType: arrayInfo.item, |
| 4759 | 4813 | try bindForLoopPattern(self, forStmt.binding, itemTy, false); |
|
| 4760 | 4814 | ||
| 4761 | 4815 | if let pat = forStmt.index { |
|
| 4762 | 4816 | try bindForLoopPattern(self, pat, Type::U32, false); |
|
| 4763 | 4817 | } |
|
| 4764 | - | // The lowerer always creates at least one internal variable for iteration, |
|
| 4765 | - | // even when the binding is a placeholder or no explicit index is given. |
|
| 4818 | + | // Reserve space for an iterator variable and a possible element pointer. |
|
| 4766 | 4819 | if let owner = self.currentFnNode { |
|
| 4767 | - | set self.nodeData.entries[owner.id].localCount += 1; |
|
| 4820 | + | set self.nodeData.entries[owner.id].localCount += 2 if hasElementPointer else 1; |
|
| 4768 | 4821 | } |
|
| 4769 | 4822 | try visitLoop(self, forStmt.body); |
|
| 4770 | 4823 | exitScope(self); |
|
| 4771 | 4824 | ||
| 4772 | 4825 | try visitOptional(self, forStmt.elseBranch, Type::Void); |
test/tests/average.ril
+12 -11
| 1 | 1 | fn w32 $average(w64 %0) { |
|
| 2 | 2 | @entry0 |
|
| 3 | 3 | load w32 %1 %0 8; |
|
| 4 | 4 | load w64 %2 %0 0; |
|
| 5 | - | jmp @loop1(0, 0); |
|
| 6 | - | @loop1(w32 %3, w32 %7) |
|
| 7 | - | br.slt w32 %3 %1 @body2 @merge3; |
|
| 8 | - | @body2 |
|
| 5 | + | zext w32 %3 %1; |
|
| 9 | 6 | mul w64 %4 %3 4; |
|
| 10 | 7 | add w64 %5 %2 %4; |
|
| 11 | - | load w32 %6 %5 0; |
|
| 12 | - | add w32 %8 %7 %6; |
|
| 13 | - | add w32 %9 %3 1; |
|
| 14 | - | jmp @loop1(%9, %8); |
|
| 8 | + | jmp @loop1(%2, 0); |
|
| 9 | + | @loop1(w64 %6, w32 %8) |
|
| 10 | + | br.ne w64 %6 %5 @body2 @merge3; |
|
| 11 | + | @body2 |
|
| 12 | + | load w32 %7 %6 0; |
|
| 13 | + | add w32 %9 %8 %7; |
|
| 14 | + | add w64 %10 %6 4; |
|
| 15 | + | jmp @loop1(%10, %9); |
|
| 15 | 16 | @merge3 |
|
| 16 | - | load w32 %11 %0 8; |
|
| 17 | - | udiv w32 %12 %7 %11; |
|
| 18 | - | ret %12; |
|
| 17 | + | load w32 %12 %0 8; |
|
| 18 | + | udiv w32 %13 %8 %12; |
|
| 19 | + | ret %13; |
|
| 19 | 20 | } |
test/tests/const.array.ril
+10 -12
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | fn w32 $main() { |
|
| 9 | 9 | @entry0 |
|
| 10 | 10 | copy %0 $NUMBERS; |
|
| 11 | - | jmp @loop1(0, 0); |
|
| 12 | - | @loop1(w32 %1, w32 %5) |
|
| 13 | - | br.slt w32 %1 4 @body2 @merge3; |
|
| 11 | + | add w64 %1 %0 16; |
|
| 12 | + | jmp @loop1(%0, 0); |
|
| 13 | + | @loop1(w64 %2, w32 %4) |
|
| 14 | + | br.ne w64 %2 %1 @body2 @merge3; |
|
| 14 | 15 | @body2 |
|
| 15 | - | mul w64 %2 %1 4; |
|
| 16 | - | add w64 %3 %0 %2; |
|
| 17 | - | sload w32 %4 %3 0; |
|
| 18 | - | add w32 %6 %5 %4; |
|
| 19 | - | add w32 %7 %1 1; |
|
| 20 | - | jmp @loop1(%7, %6); |
|
| 16 | + | sload w32 %3 %2 0; |
|
| 17 | + | add w32 %5 %4 %3; |
|
| 18 | + | add w64 %6 %2 4; |
|
| 19 | + | jmp @loop1(%6, %5); |
|
| 21 | 20 | @merge3 |
|
| 22 | - | sub w32 %8 %5 10; |
|
| 23 | - | ret %8; |
|
| 21 | + | sub w32 %7 %4 10; |
|
| 22 | + | ret %7; |
|
| 24 | 23 | } |
|
| 25 | - |
test/tests/loop.for.array.ril
+9 -10
| 1 | 1 | fn w32 $forArrayIndexed(w64 %0) { |
|
| 2 | 2 | @entry0 |
|
| 3 | - | jmp @loop1(0, 0); |
|
| 4 | - | @loop1(w32 %1, w32 %5) |
|
| 3 | + | jmp @loop1(0, %0, 0); |
|
| 4 | + | @loop1(w32 %1, w64 %2, w32 %4) |
|
| 5 | 5 | br.slt w32 %1 3 @body2 @merge3; |
|
| 6 | 6 | @body2 |
|
| 7 | - | mul w64 %2 %1 4; |
|
| 8 | - | add w64 %3 %0 %2; |
|
| 9 | - | sload w32 %4 %3 0; |
|
| 10 | - | add w32 %6 %4 %1; |
|
| 11 | - | add w32 %7 %5 %6; |
|
| 12 | - | add w32 %8 %1 1; |
|
| 13 | - | jmp @loop1(%8, %7); |
|
| 7 | + | sload w32 %3 %2 0; |
|
| 8 | + | add w32 %5 %3 %1; |
|
| 9 | + | add w32 %6 %4 %5; |
|
| 10 | + | add w32 %7 %1 1; |
|
| 11 | + | add w64 %8 %2 4; |
|
| 12 | + | jmp @loop1(%7, %8, %6); |
|
| 14 | 13 | @merge3 |
|
| 15 | - | ret %5; |
|
| 14 | + | ret %4; |
|
| 16 | 15 | } |
test/tests/loop.for.continue.ril
+13 -14
| 17 | 17 | jmp @loop1(%5, %6); |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | 20 | fn w32 $forContinueArray(w64 %0) { |
|
| 21 | 21 | @entry0 |
|
| 22 | - | jmp @loop1(0, 0); |
|
| 23 | - | @loop1(w32 %1, w32 %5) |
|
| 24 | - | br.slt w32 %1 4 @body2 @merge3; |
|
| 22 | + | add w64 %1 %0 16; |
|
| 23 | + | jmp @loop1(%0, 0); |
|
| 24 | + | @loop1(w64 %2, w32 %4) |
|
| 25 | + | br.ne w64 %2 %1 @body2 @merge3; |
|
| 25 | 26 | @body2 |
|
| 26 | - | mul w64 %2 %1 4; |
|
| 27 | - | add w64 %3 %0 %2; |
|
| 28 | - | sload w32 %4 %3 0; |
|
| 29 | - | br.eq w32 %4 0 @then4 @merge5; |
|
| 27 | + | sload w32 %3 %2 0; |
|
| 28 | + | br.eq w32 %3 0 @then4 @merge5; |
|
| 30 | 29 | @merge3 |
|
| 31 | - | ret %5; |
|
| 30 | + | ret %4; |
|
| 32 | 31 | @then4 |
|
| 33 | - | jmp @step6(%5); |
|
| 32 | + | jmp @step6(%4); |
|
| 34 | 33 | @merge5 |
|
| 35 | - | add w32 %6 %5 %4; |
|
| 36 | - | jmp @step6(%6); |
|
| 37 | - | @step6(w32 %9) |
|
| 38 | - | add w32 %8 %1 1; |
|
| 39 | - | jmp @loop1(%8, %9); |
|
| 34 | + | add w32 %5 %4 %3; |
|
| 35 | + | jmp @step6(%5); |
|
| 36 | + | @step6(w32 %8) |
|
| 37 | + | add w64 %7 %2 4; |
|
| 38 | + | jmp @loop1(%7, %8); |
|
| 40 | 39 | } |
test/tests/loop.for.pointer.rad
added
+66 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | /// Zero-size collection element. |
|
| 4 | + | record Empty: Copy {} |
|
| 5 | + | ||
| 6 | + | /// Aggregate with a stride greater than one machine word. |
|
| 7 | + | record Item: Copy { |
|
| 8 | + | /// First value. |
|
| 9 | + | first: u64, |
|
| 10 | + | /// Second value. |
|
| 11 | + | second: u64, |
|
| 12 | + | } |
|
| 13 | + | ||
| 14 | + | /// Check collection iteration through empty, short, and aggregate slices. |
|
| 15 | + | @default fn main() -> u32 { |
|
| 16 | + | let emptyStorage: [u32; 0] = []; |
|
| 17 | + | let empty: &[u32] = &emptyStorage[..]; |
|
| 18 | + | let mut count: u32 = 0; |
|
| 19 | + | for value in empty { |
|
| 20 | + | set count += value; |
|
| 21 | + | } |
|
| 22 | + | assert count == 0; |
|
| 23 | + | ||
| 24 | + | let emptyValues: [Empty; 3] = [Empty {}, Empty {}, Empty {}]; |
|
| 25 | + | let mut visits: u32 = 0; |
|
| 26 | + | for value in emptyValues { |
|
| 27 | + | assert value == Empty {}; |
|
| 28 | + | set visits += 1; |
|
| 29 | + | } |
|
| 30 | + | assert visits == 3; |
|
| 31 | + | ||
| 32 | + | let values: [u32; 4] = [3, 0, 5, 7]; |
|
| 33 | + | let slice: &[u32] = &values[..]; |
|
| 34 | + | let mut nonzero: u32 = 0; |
|
| 35 | + | for value in slice { |
|
| 36 | + | if value == 0 { |
|
| 37 | + | continue; |
|
| 38 | + | } |
|
| 39 | + | set nonzero += value; |
|
| 40 | + | } |
|
| 41 | + | assert nonzero == 15; |
|
| 42 | + | ||
| 43 | + | let mut sum: u32 = 0; |
|
| 44 | + | for value, index in slice { |
|
| 45 | + | if value == 0 { |
|
| 46 | + | continue; |
|
| 47 | + | } |
|
| 48 | + | set sum += value + index; |
|
| 49 | + | } |
|
| 50 | + | assert sum == 20; |
|
| 51 | + | ||
| 52 | + | let items: [Item; 3] = [ |
|
| 53 | + | Item { first: 1, second: 2 }, |
|
| 54 | + | Item { first: 3, second: 4 }, |
|
| 55 | + | Item { first: 5, second: 6 }, |
|
| 56 | + | ]; |
|
| 57 | + | let mut total: u64 = 0; |
|
| 58 | + | for item in items { |
|
| 59 | + | set total += item.second; |
|
| 60 | + | if item.first == 3 { |
|
| 61 | + | break; |
|
| 62 | + | } |
|
| 63 | + | } |
|
| 64 | + | assert total == 6; |
|
| 65 | + | return 0; |
|
| 66 | + | } |
test/tests/loop.for.slice.indexed.offset.bounds.rad
added
+12 -0
| 1 | + | //! returns: 133 |
|
| 2 | + | ||
| 3 | + | /// An offset index keeps its own bounds check. |
|
| 4 | + | @default fn main() -> u32 { |
|
| 5 | + | let values: [u32; 1] = [1]; |
|
| 6 | + | let slice: &[u32] = &values[..]; |
|
| 7 | + | for i in 0..slice.len { |
|
| 8 | + | let value = slice[i + 1]; |
|
| 9 | + | assert value > 0; |
|
| 10 | + | } |
|
| 11 | + | return 0; |
|
| 12 | + | } |
test/tests/loop.for.slice.indexed.other.bounds.rad
added
+14 -0
| 1 | + | //! returns: 133 |
|
| 2 | + | ||
| 3 | + | /// A different slice keeps its own bounds check. |
|
| 4 | + | @default fn main() -> u32 { |
|
| 5 | + | let values: [u32; 2] = [1, 2]; |
|
| 6 | + | let short: [u32; 1] = [3]; |
|
| 7 | + | let first: &[u32] = &values[..]; |
|
| 8 | + | let second: &[u32] = &short[..]; |
|
| 9 | + | for i in 0..first.len { |
|
| 10 | + | let value = second[i]; |
|
| 11 | + | assert value > 0; |
|
| 12 | + | } |
|
| 13 | + | return 0; |
|
| 14 | + | } |
test/tests/loop.for.slice.indexed.rad
added
+8 -0
| 1 | + | /// Sum a checked slice with the index bounded by its length. |
|
| 2 | + | fn sumIndexed(items: &[u32]) -> u32 { |
|
| 3 | + | let mut sum: u32 = 0; |
|
| 4 | + | for i in 0..items.len { |
|
| 5 | + | set sum += items[i]; |
|
| 6 | + | } |
|
| 7 | + | return sum; |
|
| 8 | + | } |
test/tests/loop.for.slice.indexed.ril
added
+16 -0
| 1 | + | fn w32 $sumIndexed(w64 %0) { |
|
| 2 | + | @entry0 |
|
| 3 | + | load w32 %1 %0 8; |
|
| 4 | + | load w64 %2 %0 0; |
|
| 5 | + | jmp @loop1(0, 0, %2); |
|
| 6 | + | @loop1(w32 %3, w32 %4, w64 %5) |
|
| 7 | + | br.ult w32 %3 %1 @body2 @merge3; |
|
| 8 | + | @body2 |
|
| 9 | + | load w32 %6 %5 0; |
|
| 10 | + | add w32 %7 %4 %6; |
|
| 11 | + | add w32 %8 %3 1; |
|
| 12 | + | add w64 %9 %5 4; |
|
| 13 | + | jmp @loop1(%8, %7, %9); |
|
| 14 | + | @merge3 |
|
| 15 | + | ret %4; |
|
| 16 | + | } |
test/tests/loop.for.slice.indexed.runtime.rad
added
+55 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | /// Aggregate with a sixteen-byte element stride. |
|
| 4 | + | record Pair: Copy { |
|
| 5 | + | /// First value. |
|
| 6 | + | first: u64, |
|
| 7 | + | /// Second value. |
|
| 8 | + | second: u64, |
|
| 9 | + | } |
|
| 10 | + | ||
| 11 | + | /// Exercise exact indexed traversal through checked slices. |
|
| 12 | + | @default fn main() -> u32 { |
|
| 13 | + | let emptyStorage: [u32; 0] = []; |
|
| 14 | + | let empty: &[u32] = &emptyStorage[..]; |
|
| 15 | + | let mut count: u32 = 0; |
|
| 16 | + | for i in 0..empty.len { |
|
| 17 | + | set count += empty[i]; |
|
| 18 | + | } |
|
| 19 | + | assert count == 0; |
|
| 20 | + | ||
| 21 | + | let values: [u32; 3] = [2, 3, 5]; |
|
| 22 | + | let slice: &[u32] = &values[..]; |
|
| 23 | + | let mut sum: u32 = 0; |
|
| 24 | + | for i in 0..slice.len { |
|
| 25 | + | if i == 1 { |
|
| 26 | + | continue; |
|
| 27 | + | } |
|
| 28 | + | set sum += slice[i]; |
|
| 29 | + | } |
|
| 30 | + | assert sum == 7; |
|
| 31 | + | ||
| 32 | + | let pairs: [Pair; 3] = [ |
|
| 33 | + | Pair { first: 1, second: 2 }, |
|
| 34 | + | Pair { first: 3, second: 4 }, |
|
| 35 | + | Pair { first: 5, second: 6 }, |
|
| 36 | + | ]; |
|
| 37 | + | let pairSlice: &[Pair] = &pairs[..]; |
|
| 38 | + | let mut total: u64 = 0; |
|
| 39 | + | for i in 0..pairSlice.len { |
|
| 40 | + | set total += pairSlice[i].second; |
|
| 41 | + | if i == 1 { |
|
| 42 | + | break; |
|
| 43 | + | } |
|
| 44 | + | } |
|
| 45 | + | assert total == 6; |
|
| 46 | + | ||
| 47 | + | let mut nested: u32 = 0; |
|
| 48 | + | for i in 0..slice.len { |
|
| 49 | + | for j in 0..2 { |
|
| 50 | + | set nested += slice[i] + j; |
|
| 51 | + | } |
|
| 52 | + | } |
|
| 53 | + | assert nested == 23; |
|
| 54 | + | return 0; |
|
| 55 | + | } |
test/tests/loop.for.slice.indexed.shadow.bounds.rad
added
+13 -0
| 1 | + | //! returns: 133 |
|
| 2 | + | ||
| 3 | + | /// A shadowed index keeps its own bounds check. |
|
| 4 | + | @default fn main() -> u32 { |
|
| 5 | + | let values: [u32; 1] = [1]; |
|
| 6 | + | let slice: &[u32] = &values[..]; |
|
| 7 | + | for i in 0..slice.len { |
|
| 8 | + | let i: u32 = 1; |
|
| 9 | + | let value = slice[i]; |
|
| 10 | + | assert value > 0; |
|
| 11 | + | } |
|
| 12 | + | return 0; |
|
| 13 | + | } |
test/tests/loop.for.slice.indexed.source.shadow.bounds.rad
added
+14 -0
| 1 | + | //! returns: 133 |
|
| 2 | + | ||
| 3 | + | /// A shadowed slice keeps its own bounds check. |
|
| 4 | + | @default fn main() -> u32 { |
|
| 5 | + | let values: [u32; 2] = [1, 2]; |
|
| 6 | + | let short: [u32; 1] = [3]; |
|
| 7 | + | let slice: &[u32] = &values[..]; |
|
| 8 | + | for i in 0..slice.len { |
|
| 9 | + | let slice: &[u32] = &short[..]; |
|
| 10 | + | let value = slice[i]; |
|
| 11 | + | assert value > 0; |
|
| 12 | + | } |
|
| 13 | + | return 0; |
|
| 14 | + | } |
test/tests/loop.for.slice.pointer.bound.rad
added
+8 -0
| 1 | + | /// Sum a checked slice without an index binding. |
|
| 2 | + | fn sumValues(values: &[u32]) -> u32 { |
|
| 3 | + | let mut sum: u32 = 0; |
|
| 4 | + | for value in values { |
|
| 5 | + | set sum += value; |
|
| 6 | + | } |
|
| 7 | + | return sum; |
|
| 8 | + | } |
test/tests/loop.for.slice.pointer.bound.ril
added
+18 -0
| 1 | + | fn w32 $sumValues(w64 %0) { |
|
| 2 | + | @entry0 |
|
| 3 | + | load w32 %1 %0 8; |
|
| 4 | + | load w64 %2 %0 0; |
|
| 5 | + | zext w32 %3 %1; |
|
| 6 | + | mul w64 %4 %3 4; |
|
| 7 | + | add w64 %5 %2 %4; |
|
| 8 | + | jmp @loop1(%2, 0); |
|
| 9 | + | @loop1(w64 %6, w32 %8) |
|
| 10 | + | br.ne w64 %6 %5 @body2 @merge3; |
|
| 11 | + | @body2 |
|
| 12 | + | load w32 %7 %6 0; |
|
| 13 | + | add w32 %9 %8 %7; |
|
| 14 | + | add w64 %10 %6 4; |
|
| 15 | + | jmp @loop1(%10, %9); |
|
| 16 | + | @merge3 |
|
| 17 | + | ret %8; |
|
| 18 | + | } |
test/tests/loop.for.slice.ril
+9 -10
| 1 | 1 | fn w32 $forSliceIndexed(w64 %0) { |
|
| 2 | 2 | @entry0 |
|
| 3 | 3 | load w32 %1 %0 8; |
|
| 4 | 4 | load w64 %2 %0 0; |
|
| 5 | - | jmp @loop1(0, 0); |
|
| 6 | - | @loop1(w32 %3, w32 %7) |
|
| 5 | + | jmp @loop1(0, %2, 0); |
|
| 6 | + | @loop1(w32 %3, w64 %4, w32 %6) |
|
| 7 | 7 | br.slt w32 %3 %1 @body2 @merge3; |
|
| 8 | 8 | @body2 |
|
| 9 | - | mul w64 %4 %3 4; |
|
| 10 | - | add w64 %5 %2 %4; |
|
| 11 | - | sload w32 %6 %5 0; |
|
| 12 | - | add w32 %8 %6 %3; |
|
| 13 | - | add w32 %9 %7 %8; |
|
| 14 | - | add w32 %10 %3 1; |
|
| 15 | - | jmp @loop1(%10, %9); |
|
| 9 | + | sload w32 %5 %4 0; |
|
| 10 | + | add w32 %7 %5 %3; |
|
| 11 | + | add w32 %8 %6 %7; |
|
| 12 | + | add w32 %9 %3 1; |
|
| 13 | + | add w64 %10 %4 4; |
|
| 14 | + | jmp @loop1(%9, %10, %8); |
|
| 16 | 15 | @merge3 |
|
| 17 | - | ret %7; |
|
| 16 | + | ret %6; |
|
| 18 | 17 | } |