Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/abstract/BaseRainlangExtern.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import {IIntegrityToolingV1} from "rain.sol.codegen/interface/IIntegrityToolingV1.sol";
import {IOpcodeToolingV1} from "rain.sol.codegen/interface/IOpcodeToolingV1.sol";
import {ExternOpcodeOutOfRange, ExternPointersMismatch, ExternOpcodePointersEmpty} from "../error/ErrExtern.sol";
import {OPCODE_FUNCTION_POINTER_SHIFT} from "../lib/eval/LibEval.sol";

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

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

function(OperandV2, uint256, uint256) internal pure returns (uint256, uint256) f;
assembly ("memory-safe") {
f := shr(0xf0, mload(add(fPointersStart, mul(opcode, 2))))
f := shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(add(fPointersStart, mul(opcode, 2))))
}
(actualInputs, actualOutputs) = f(operand, expectedInputs, expectedOutputs);
}
Expand Down
48 changes: 38 additions & 10 deletions src/lib/eval/LibEval.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ import {OperandV2, StackItem} from "rain.interpreter.interface/interface/IInterp

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

/// @dev Shift to extract a packed 2-byte function pointer from the high bits
/// of a 256-bit mload. `shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(...))` yields
/// the 16-bit pointer value.
uint256 constant OPCODE_FUNCTION_POINTER_SHIFT = 0xf0;

/// @dev Shift to extract a packed 2-byte source offset from the bytecode
/// header. Same width as function pointers but semantically distinct — these
/// are relative offsets into the bytecode, not function pointers.
uint256 constant SOURCE_OFFSET_SHIFT = 0xf0;

library LibEval {
using LibMemoryKV for MemoryKV;

Expand Down Expand Up @@ -64,7 +74,7 @@ library LibEval {
// Find start of sources.
let sourcesStart := add(cursor, mul(sourcesLength, 2))
// Find relative pointer to source.
let sourcesPointer := shr(0xf0, mload(add(cursor, mul(sourceIndex, 2))))
let sourcesPointer := shr(SOURCE_OFFSET_SHIFT, mload(add(cursor, mul(sourceIndex, 2))))
// Move cursor to start of source.
cursor := add(sourcesStart, sourcesPointer)
// Calculate the end.
Expand Down Expand Up @@ -97,56 +107,71 @@ library LibEval {
// f needs to be looked up from the fn pointers table.
// operand is 3 bytes.
assembly ("memory-safe") {
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(0, word), fsCount), 2))))
f := shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(add(fPointersStart, mul(mod(byte(0, word), fsCount), 2))))
operand := and(shr(0xe0, word), 0xFFFFFF)
}
stackTop = f(state, operand, stackTop);

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

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

// Bytes [16, 19].
assembly ("memory-safe") {
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(12, word), fsCount), 2))))
f := shr(
OPCODE_FUNCTION_POINTER_SHIFT,
mload(add(fPointersStart, mul(mod(byte(12, word), fsCount), 2)))
)
operand := and(shr(0x80, word), 0xFFFFFF)
}
stackTop = f(state, operand, stackTop);

// Bytes [12, 15].
assembly ("memory-safe") {
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(16, word), fsCount), 2))))
f := shr(
OPCODE_FUNCTION_POINTER_SHIFT,
mload(add(fPointersStart, mul(mod(byte(16, word), fsCount), 2)))
)
operand := and(shr(0x60, word), 0xFFFFFF)
}
stackTop = f(state, operand, stackTop);

// Bytes [8, 11].
assembly ("memory-safe") {
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(20, word), fsCount), 2))))
f := shr(
OPCODE_FUNCTION_POINTER_SHIFT,
mload(add(fPointersStart, mul(mod(byte(20, word), fsCount), 2)))
)
operand := and(shr(0x40, word), 0xFFFFFF)
}
stackTop = f(state, operand, stackTop);

// Bytes [4, 7].
assembly ("memory-safe") {
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(24, word), fsCount), 2))))
f := shr(
OPCODE_FUNCTION_POINTER_SHIFT,
mload(add(fPointersStart, mul(mod(byte(24, word), fsCount), 2)))
)
operand := and(shr(0x20, word), 0xFFFFFF)
}
stackTop = f(state, operand, stackTop);

// Bytes [0, 3].
assembly ("memory-safe") {
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(28, word), fsCount), 2))))
f := shr(
OPCODE_FUNCTION_POINTER_SHIFT,
mload(add(fPointersStart, mul(mod(byte(28, word), fsCount), 2)))
)
operand := and(word, 0xFFFFFF)
}
stackTop = f(state, operand, stackTop);
Expand All @@ -163,7 +188,10 @@ library LibEval {
while (cursor < end) {
assembly ("memory-safe") {
word := mload(cursor)
f := shr(0xf0, mload(add(fPointersStart, mul(mod(byte(28, word), fsCount), 2))))
f := shr(
OPCODE_FUNCTION_POINTER_SHIFT,
mload(add(fPointersStart, mul(mod(byte(28, word), fsCount), 2)))
)
// 3 bytes mask.
operand := and(word, 0xFFFFFF)
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/integrity/LibIntegrityCheck.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import {BadOpInputsLength, BadOpOutputsLength} from "rain.interpreter.interface/error/ErrIntegrity.sol";
import {LibBytecode} from "rain.interpreter.interface/lib/bytecode/LibBytecode.sol";
import {OperandV2} from "rain.interpreter.interface/interface/IInterpreterV4.sol";
import {OPCODE_FUNCTION_POINTER_SHIFT} from "../eval/LibEval.sol";

/// @notice Tracks the state of the integrity check walk over a single source.
/// @param stackIndex Current logical stack depth. Increases with opcode
Expand Down Expand Up @@ -162,7 +163,7 @@ library LibIntegrityCheck {
revert OpcodeOutOfRange(state.opIndex, opcodeIndex, fsCount);
}
assembly ("memory-safe") {
f := shr(0xf0, mload(add(fPointersStart, mul(opcodeIndex, 2))))
f := shr(OPCODE_FUNCTION_POINTER_SHIFT, mload(add(fPointersStart, mul(opcodeIndex, 2))))
}
(uint256 calcOpInputs, uint256 calcOpOutputs) = f(state, operand);
if (calcOpInputs != bytecodeOpInputs) {
Expand Down
5 changes: 3 additions & 2 deletions src/lib/parse/LibParse.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ import {
FSM_DEFAULT,
FSM_ACTIVE_SOURCE_MASK,
FSM_WORD_END_MASK,
PARSE_STATE_PAREN_TRACKER0_OFFSET
PARSE_STATE_PAREN_TRACKER0_OFFSET,
PAREN_POINTER_SHIFT
} from "./LibParseState.sol";
import {LibParsePragma} from "./LibParsePragma.sol";
import {LibParseInterstitial} from "./LibParseInterstitial.sol";
Expand Down Expand Up @@ -380,7 +381,7 @@ library LibParse {
// Add 1 to sandwich the inputs byte between
// the opcode index byte and the operand low
// bytes.
add(1, shr(0xf0, mload(add(add(stateOffset, 2), parenOffset)))),
add(1, shr(PAREN_POINTER_SHIFT, mload(add(add(stateOffset, 2), parenOffset)))),
// Store the input counter, which is 2 bytes
// after the operand write pointer.
byte(0, mload(add(add(stateOffset, 4), parenOffset)))
Expand Down
8 changes: 6 additions & 2 deletions src/lib/parse/LibParseInterstitial.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import {MalformedCommentStart, UnclosedComment} from "../../error/ErrParse.sol";
import {LibParseError} from "./LibParseError.sol";
import {LibParseChar} from "rain.string/lib/parse/LibParseChar.sol";

/// @dev Shift to extract a packed 2-byte comment delimiter sequence from the
/// high bits of a 256-bit mload.
uint256 constant COMMENT_SEQUENCE_SHIFT = 0xf0;

/// @title LibParseInterstitial
/// @notice Handles whitespace and comment skipping between meaningful tokens
/// during parsing.
Expand Down Expand Up @@ -46,7 +50,7 @@ library LibParseInterstitial {
// First check the comment opening sequence is not malformed.
uint256 startSequence;
assembly ("memory-safe") {
startSequence := shr(0xf0, mload(cursor))
startSequence := shr(COMMENT_SEQUENCE_SHIFT, mload(cursor))
}
if (startSequence != COMMENT_START_SEQUENCE) {
revert MalformedCommentStart(state.parseErrorOffset(cursor));
Expand All @@ -68,7 +72,7 @@ library LibParseInterstitial {
// Check the sequence.
uint256 endSequence;
assembly ("memory-safe") {
endSequence := shr(0xf0, mload(sub(cursor, 1)))
endSequence := shr(COMMENT_SEQUENCE_SHIFT, mload(sub(cursor, 1)))
}
if (endSequence == COMMENT_END_SEQUENCE) {
// We found the end of the comment.
Expand Down
12 changes: 10 additions & 2 deletions src/lib/parse/LibParseState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ uint256 constant SUB_PARSER_POINTER_SHIFT = 0xA0;
/// 0x20.
uint256 constant EMPTY_ACTIVE_SOURCE = 0x20;

/// @dev Shift to extract a packed 2-byte active source pointer from the high
/// bits of a 256-bit mload during parse-time source traversal.
uint256 constant ACTIVE_SOURCE_POINTER_SHIFT = 0xf0;

/// @dev Shift to extract a packed 2-byte paren tracking pointer from the high
/// bits of a 256-bit mload during operand processing.
uint256 constant PAREN_POINTER_SHIFT = 0xf0;

/// @dev Bit 0 of the FSM. When set, the parser is in "yang" state (building
/// an RHS word). When clear, the parser is in "yin" state (between words).
uint256 constant FSM_YANG_MASK = 1;
Expand Down Expand Up @@ -496,7 +504,7 @@ library LibParseState {
// is handled on allocation in `newActiveSourcePointer`.
if (itemSourceHead % 0x20 == 0x1c) {
assembly ("memory-safe") {
itemSourceHead := shr(0xf0, mload(itemSourceHead))
itemSourceHead := shr(ACTIVE_SOURCE_POINTER_SHIFT, mload(itemSourceHead))
}
}
uint256 opInputs;
Expand Down Expand Up @@ -1002,7 +1010,7 @@ library LibParseState {
let relativePointer := and(mload(add(bytecode, add(3, mul(i, 2)))), 0xFFFF)
targetPointer := add(sourcesStart, relativePointer)
let tmpPrefix := mload(targetPointer)
sourcePointer := add(0x20, shr(0xf0, tmpPrefix))
sourcePointer := add(0x20, shr(ACTIVE_SOURCE_POINTER_SHIFT, tmpPrefix))
length := and(shr(0xe0, tmpPrefix), 0xFFFF)
}
LibMemCpy.unsafeCopyBytesTo(sourcePointer, targetPointer, length);
Expand Down
3 changes: 2 additions & 1 deletion src/lib/state/LibInterpreterStateDataContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {FullyQualifiedNamespace} from "rain.interpreter.interface/interface/IInt
import {IInterpreterStoreV3} from "rain.interpreter.interface/interface/IInterpreterStoreV3.sol";

import {InterpreterState} from "./LibInterpreterState.sol";
import {SOURCE_OFFSET_SHIFT} from "../eval/LibEval.sol";

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

Expand Down
Loading