Skip to content

Commit e5dc868

Browse files
Merge pull request #481 from rainlanguage/extract-pointer-shift-constant
Extract named constants for 0xf0 pointer shifts
2 parents 1a91779 + 5cc2f10 commit e5dc868

7 files changed

Lines changed: 64 additions & 20 deletions

File tree

src/abstract/BaseRainlangExtern.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import {IIntegrityToolingV1} from "rain.sol.codegen/interface/IIntegrityToolingV1.sol";
1414
import {IOpcodeToolingV1} from "rain.sol.codegen/interface/IOpcodeToolingV1.sol";
1515
import {ExternOpcodeOutOfRange, ExternPointersMismatch, ExternOpcodePointersEmpty} from "../error/ErrExtern.sol";
16+
import {OPCODE_FUNCTION_POINTER_SHIFT} from "../lib/eval/LibEval.sol";
1617

1718
/// @dev Empty opcode function pointers constant. Inheriting contracts should
1819
/// create their own constant and override `opcodeFunctionPointers` to use
@@ -73,7 +74,7 @@ abstract contract BaseRainlangExtern is IInterpreterExternV4, IIntegrityToolingV
7374

7475
function(OperandV2, StackItem[] memory) internal view returns (StackItem[] memory) f;
7576
assembly ("memory-safe") {
76-
f := shr(0xf0, mload(add(fPointersStart, mul(mod(opcode, fsCount), 2))))
77+
f := shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(add(fPointersStart, mul(mod(opcode, fsCount), 2))))
7778
}
7879
outputs = f(operand, inputs);
7980
}
@@ -102,7 +103,7 @@ abstract contract BaseRainlangExtern is IInterpreterExternV4, IIntegrityToolingV
102103

103104
function(OperandV2, uint256, uint256) internal pure returns (uint256, uint256) f;
104105
assembly ("memory-safe") {
105-
f := shr(0xf0, mload(add(fPointersStart, mul(opcode, 2))))
106+
f := shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(add(fPointersStart, mul(opcode, 2))))
106107
}
107108
(actualInputs, actualOutputs) = f(operand, expectedInputs, expectedOutputs);
108109
}

src/lib/eval/LibEval.sol

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ import {OperandV2, StackItem} from "rain.interpreter.interface/interface/IInterp
1212

1313
import {InputsLengthMismatch} from "../../error/ErrEval.sol";
1414

15+
/// @dev Shift to extract a packed 2-byte function pointer from the high bits
16+
/// of a 256-bit mload. `shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(...))` yields
17+
/// the 16-bit pointer value.
18+
uint256 constant OPCODE_FUNCTION_POINTER_SHIFT = 0xf0;
19+
20+
/// @dev Shift to extract a packed 2-byte source offset from the bytecode
21+
/// header. Same width as function pointers but semantically distinct — these
22+
/// are relative offsets into the bytecode, not function pointers.
23+
uint256 constant SOURCE_OFFSET_SHIFT = 0xf0;
24+
1525
library LibEval {
1626
using LibMemoryKV for MemoryKV;
1727

@@ -64,7 +74,7 @@ library LibEval {
6474
// Find start of sources.
6575
let sourcesStart := add(cursor, mul(sourcesLength, 2))
6676
// Find relative pointer to source.
67-
let sourcesPointer := shr(0xf0, mload(add(cursor, mul(sourceIndex, 2))))
77+
let sourcesPointer := shr(SOURCE_OFFSET_SHIFT, mload(add(cursor, mul(sourceIndex, 2))))
6878
// Move cursor to start of source.
6979
cursor := add(sourcesStart, sourcesPointer)
7080
// Calculate the end.
@@ -97,56 +107,71 @@ library LibEval {
97107
// f needs to be looked up from the fn pointers table.
98108
// operand is 3 bytes.
99109
assembly ("memory-safe") {
100-
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(0, word), fsCount), 2))))
110+
f := shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(add(fPointersStart, mul(mod(byte(0, word), fsCount), 2))))
101111
operand := and(shr(0xe0, word), 0xFFFFFF)
102112
}
103113
stackTop = f(state, operand, stackTop);
104114

105115
// Bytes [24, 27].
106116
assembly ("memory-safe") {
107-
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(4, word), fsCount), 2))))
117+
f := shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(add(fPointersStart, mul(mod(byte(4, word), fsCount), 2))))
108118
operand := and(shr(0xc0, word), 0xFFFFFF)
109119
}
110120
stackTop = f(state, operand, stackTop);
111121

112122
// Bytes [20, 23].
113123
assembly ("memory-safe") {
114-
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(8, word), fsCount), 2))))
124+
f := shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(add(fPointersStart, mul(mod(byte(8, word), fsCount), 2))))
115125
operand := and(shr(0xa0, word), 0xFFFFFF)
116126
}
117127
stackTop = f(state, operand, stackTop);
118128

119129
// Bytes [16, 19].
120130
assembly ("memory-safe") {
121-
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(12, word), fsCount), 2))))
131+
f := shr(
132+
OPCODE_FUNCTION_POINTER_SHIFT,
133+
mload(add(fPointersStart, mul(mod(byte(12, word), fsCount), 2)))
134+
)
122135
operand := and(shr(0x80, word), 0xFFFFFF)
123136
}
124137
stackTop = f(state, operand, stackTop);
125138

126139
// Bytes [12, 15].
127140
assembly ("memory-safe") {
128-
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(16, word), fsCount), 2))))
141+
f := shr(
142+
OPCODE_FUNCTION_POINTER_SHIFT,
143+
mload(add(fPointersStart, mul(mod(byte(16, word), fsCount), 2)))
144+
)
129145
operand := and(shr(0x60, word), 0xFFFFFF)
130146
}
131147
stackTop = f(state, operand, stackTop);
132148

133149
// Bytes [8, 11].
134150
assembly ("memory-safe") {
135-
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(20, word), fsCount), 2))))
151+
f := shr(
152+
OPCODE_FUNCTION_POINTER_SHIFT,
153+
mload(add(fPointersStart, mul(mod(byte(20, word), fsCount), 2)))
154+
)
136155
operand := and(shr(0x40, word), 0xFFFFFF)
137156
}
138157
stackTop = f(state, operand, stackTop);
139158

140159
// Bytes [4, 7].
141160
assembly ("memory-safe") {
142-
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(24, word), fsCount), 2))))
161+
f := shr(
162+
OPCODE_FUNCTION_POINTER_SHIFT,
163+
mload(add(fPointersStart, mul(mod(byte(24, word), fsCount), 2)))
164+
)
143165
operand := and(shr(0x20, word), 0xFFFFFF)
144166
}
145167
stackTop = f(state, operand, stackTop);
146168

147169
// Bytes [0, 3].
148170
assembly ("memory-safe") {
149-
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(28, word), fsCount), 2))))
171+
f := shr(
172+
OPCODE_FUNCTION_POINTER_SHIFT,
173+
mload(add(fPointersStart, mul(mod(byte(28, word), fsCount), 2)))
174+
)
150175
operand := and(word, 0xFFFFFF)
151176
}
152177
stackTop = f(state, operand, stackTop);
@@ -163,7 +188,10 @@ library LibEval {
163188
while (cursor < end) {
164189
assembly ("memory-safe") {
165190
word := mload(cursor)
166-
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(28, word), fsCount), 2))))
191+
f := shr(
192+
OPCODE_FUNCTION_POINTER_SHIFT,
193+
mload(add(fPointersStart, mul(mod(byte(28, word), fsCount), 2)))
194+
)
167195
// 3 bytes mask.
168196
operand := and(word, 0xFFFFFF)
169197
}

src/lib/integrity/LibIntegrityCheck.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import {BadOpInputsLength, BadOpOutputsLength} from "rain.interpreter.interface/error/ErrIntegrity.sol";
1515
import {LibBytecode} from "rain.interpreter.interface/lib/bytecode/LibBytecode.sol";
1616
import {OperandV2} from "rain.interpreter.interface/interface/IInterpreterV4.sol";
17+
import {OPCODE_FUNCTION_POINTER_SHIFT} from "../eval/LibEval.sol";
1718

1819
/// @notice Tracks the state of the integrity check walk over a single source.
1920
/// @param stackIndex Current logical stack depth. Increases with opcode
@@ -162,7 +163,7 @@ library LibIntegrityCheck {
162163
revert OpcodeOutOfRange(state.opIndex, opcodeIndex, fsCount);
163164
}
164165
assembly ("memory-safe") {
165-
f := shr(0xf0, mload(add(fPointersStart, mul(opcodeIndex, 2))))
166+
f := shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(add(fPointersStart, mul(opcodeIndex, 2))))
166167
}
167168
(uint256 calcOpInputs, uint256 calcOpOutputs) = f(state, operand);
168169
if (calcOpInputs != bytecodeOpInputs) {

src/lib/parse/LibParse.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ import {
4444
FSM_DEFAULT,
4545
FSM_ACTIVE_SOURCE_MASK,
4646
FSM_WORD_END_MASK,
47-
PARSE_STATE_PAREN_TRACKER0_OFFSET
47+
PARSE_STATE_PAREN_TRACKER0_OFFSET,
48+
PAREN_POINTER_SHIFT
4849
} from "./LibParseState.sol";
4950
import {LibParsePragma} from "./LibParsePragma.sol";
5051
import {LibParseInterstitial} from "./LibParseInterstitial.sol";
@@ -380,7 +381,7 @@ library LibParse {
380381
// Add 1 to sandwich the inputs byte between
381382
// the opcode index byte and the operand low
382383
// bytes.
383-
add(1, shr(0xf0, mload(add(add(stateOffset, 2), parenOffset)))),
384+
add(1, shr(PAREN_POINTER_SHIFT, mload(add(add(stateOffset, 2), parenOffset)))),
384385
// Store the input counter, which is 2 bytes
385386
// after the operand write pointer.
386387
byte(0, mload(add(add(stateOffset, 4), parenOffset)))

src/lib/parse/LibParseInterstitial.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ import {MalformedCommentStart, UnclosedComment} from "../../error/ErrParse.sol";
1414
import {LibParseError} from "./LibParseError.sol";
1515
import {LibParseChar} from "rain.string/lib/parse/LibParseChar.sol";
1616

17+
/// @dev Shift to extract a packed 2-byte comment delimiter sequence from the
18+
/// high bits of a 256-bit mload.
19+
uint256 constant COMMENT_SEQUENCE_SHIFT = 0xf0;
20+
1721
/// @title LibParseInterstitial
1822
/// @notice Handles whitespace and comment skipping between meaningful tokens
1923
/// during parsing.
@@ -46,7 +50,7 @@ library LibParseInterstitial {
4650
// First check the comment opening sequence is not malformed.
4751
uint256 startSequence;
4852
assembly ("memory-safe") {
49-
startSequence := shr(0xf0, mload(cursor))
53+
startSequence := shr(COMMENT_SEQUENCE_SHIFT, mload(cursor))
5054
}
5155
if (startSequence != COMMENT_START_SEQUENCE) {
5256
revert MalformedCommentStart(state.parseErrorOffset(cursor));
@@ -68,7 +72,7 @@ library LibParseInterstitial {
6872
// Check the sequence.
6973
uint256 endSequence;
7074
assembly ("memory-safe") {
71-
endSequence := shr(0xf0, mload(sub(cursor, 1)))
75+
endSequence := shr(COMMENT_SEQUENCE_SHIFT, mload(sub(cursor, 1)))
7276
}
7377
if (endSequence == COMMENT_END_SEQUENCE) {
7478
// We found the end of the comment.

src/lib/parse/LibParseState.sol

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ uint256 constant SUB_PARSER_POINTER_SHIFT = 0xA0;
3737
/// 0x20.
3838
uint256 constant EMPTY_ACTIVE_SOURCE = 0x20;
3939

40+
/// @dev Shift to extract a packed 2-byte active source pointer from the high
41+
/// bits of a 256-bit mload during parse-time source traversal.
42+
uint256 constant ACTIVE_SOURCE_POINTER_SHIFT = 0xf0;
43+
44+
/// @dev Shift to extract a packed 2-byte paren tracking pointer from the high
45+
/// bits of a 256-bit mload during operand processing.
46+
uint256 constant PAREN_POINTER_SHIFT = 0xf0;
47+
4048
/// @dev Bit 0 of the FSM. When set, the parser is in "yang" state (building
4149
/// an RHS word). When clear, the parser is in "yin" state (between words).
4250
uint256 constant FSM_YANG_MASK = 1;
@@ -496,7 +504,7 @@ library LibParseState {
496504
// is handled on allocation in `newActiveSourcePointer`.
497505
if (itemSourceHead % 0x20 == 0x1c) {
498506
assembly ("memory-safe") {
499-
itemSourceHead := shr(0xf0, mload(itemSourceHead))
507+
itemSourceHead := shr(ACTIVE_SOURCE_POINTER_SHIFT, mload(itemSourceHead))
500508
}
501509
}
502510
uint256 opInputs;
@@ -1002,7 +1010,7 @@ library LibParseState {
10021010
let relativePointer := and(mload(add(bytecode, add(3, mul(i, 2)))), 0xFFFF)
10031011
targetPointer := add(sourcesStart, relativePointer)
10041012
let tmpPrefix := mload(targetPointer)
1005-
sourcePointer := add(0x20, shr(0xf0, tmpPrefix))
1013+
sourcePointer := add(0x20, shr(ACTIVE_SOURCE_POINTER_SHIFT, tmpPrefix))
10061014
length := and(shr(0xe0, tmpPrefix), 0xFFFF)
10071015
}
10081016
LibMemCpy.unsafeCopyBytesTo(sourcePointer, targetPointer, length);

src/lib/state/LibInterpreterStateDataContract.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {FullyQualifiedNamespace} from "rain.interpreter.interface/interface/IInt
1010
import {IInterpreterStoreV3} from "rain.interpreter.interface/interface/IInterpreterStoreV3.sol";
1111

1212
import {InterpreterState} from "./LibInterpreterState.sol";
13+
import {SOURCE_OFFSET_SHIFT} from "../eval/LibEval.sol";
1314

1415
library LibInterpreterStateDataContract {
1516
using LibBytes for bytes;
@@ -118,7 +119,7 @@ library LibInterpreterStateDataContract {
118119
} {
119120
// The stack size is in the prefix of the source data, which
120121
// is behind a relative pointer in the bytecode prefix.
121-
let sourcePointer := add(sourcesStart, shr(0xf0, mload(cursor)))
122+
let sourcePointer := add(sourcesStart, shr(SOURCE_OFFSET_SHIFT, mload(cursor)))
122123
// Stack size is the second byte of the source prefix.
123124
let stackSize := byte(1, mload(sourcePointer))
124125

0 commit comments

Comments
 (0)