Skip to content
This repository has been archived by the owner on Sep 5, 2023. It is now read-only.

Commit

Permalink
translate int conversions to Cairo 1 (#1084)
Browse files Browse the repository at this point in the history
Instead of generating Cairo functions, we use generic capabilities of
   Cairo 1 to write a single static function in warplib.

Additionally we remove some usages of old functions, but not all of
   them, since they are tied to much into some subsystems, like
   calldata and storage. This will be finished in future work.
  • Loading branch information
temyurchenko authored Jun 8, 2023
1 parent bbf4834 commit 46821d3
Show file tree
Hide file tree
Showing 17 changed files with 220 additions and 152 deletions.
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,6 @@ warplib/src/maths/lt_signed.cairo
# bitwise_or - handwritten
warplib/src/maths/bitwise_not.cairo

# ---conversions---
warplib/src/conversions/int_conversions.cairo

# ---external-input-input-checks---
warplib/src/external_input_check/external_input_check_ints.cairo
# external_input_check_address.cairo - handwritten
Expand All @@ -226,4 +223,3 @@ warplib/src/warp_memory/bytes.cairo

tests/cli/starknet_open_zeppelin_accounts.json
tests/cli/starknet_open_zeppelin_accounts.json.backup

5 changes: 2 additions & 3 deletions src/cairoUtilFuncGen/calldata/calldataToStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { printTypeNode } from '../../utils/astPrinter';
import { CairoDynArray, CairoType, TypeConversionContext } from '../../utils/cairoTypeSystem';
import { NotSupportedYetError } from '../../utils/errors';
import { createCairoGeneratedFunction, createCallToFunction } from '../../utils/functionGeneration';
import { WARP_UINT256 } from '../../utils/importPaths';
import { getElementType, safeGetNodeType } from '../../utils/nodeTypeProcessing';
import { mapRange, narrowBigIntSafe, typeNameFromTypeNode } from '../../utils/utils';
import { add, delegateBasedOnType, GeneratedFunctionInfo, StringIndexedFuncGen } from '../base';
Expand Down Expand Up @@ -179,7 +178,7 @@ export class CalldataToStorageGen extends StringIndexedFuncGen {
}
}
func ${funcName}(loc : felt, dyn_array_struct : ${structDef.name}) -> (loc : felt){
func ${funcName}(loc : felt, dyn_array_struct : ${structDef.name}) -> (loc : felt){
alloc_locals;
let (len_uint256) = warp_uint256(dyn_array_struct.len);
${lenName}.write(loc, len_uint256);
Expand All @@ -191,7 +190,7 @@ export class CalldataToStorageGen extends StringIndexedFuncGen {
return {
name: funcName,
code: code,
functionsCalled: [this.requireImport(...WARP_UINT256), dynArray, dynArrayLength, writeDef],
functionsCalled: [dynArray, dynArrayLength, writeDef],
};
}

Expand Down
8 changes: 2 additions & 6 deletions src/cairoUtilFuncGen/storage/storageToCalldata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { printTypeNode } from '../../utils/astPrinter';
import { CairoDynArray, CairoType, TypeConversionContext } from '../../utils/cairoTypeSystem';
import { NotSupportedYetError } from '../../utils/errors';
import { createCairoGeneratedFunction, createCallToFunction } from '../../utils/functionGeneration';
import { ALLOC, U128_FROM_FELT, WARP_UINT256 } from '../../utils/importPaths';
import { ALLOC, U128_FROM_FELT } from '../../utils/importPaths';
import { getElementType, safeGetNodeType } from '../../utils/nodeTypeProcessing';
import { mapRange, narrowBigIntSafe, typeNameFromTypeNode } from '../../utils/utils';
import { add, delegateBasedOnType, GeneratedFunctionInfo, StringIndexedFuncGen } from '../base';
Expand Down Expand Up @@ -188,11 +188,7 @@ export class StorageToCalldataGen extends StringIndexedFuncGen {
}
`;

const importedFuncs = [
this.requireImport(...WARP_UINT256),
this.requireImport(...U128_FROM_FELT),
this.requireImport(...ALLOC),
];
const importedFuncs = [this.requireImport(...U128_FROM_FELT), this.requireImport(...ALLOC)];

const funcInfo: GeneratedFunctionInfo = {
name: funcName,
Expand Down
22 changes: 21 additions & 1 deletion src/passes/cairoUtilImporter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AddressType,
ElementaryTypeName,
FunctionCall,
IntType,
Literal,
MemberAccess,
Expand All @@ -11,8 +12,16 @@ import {
} from 'solc-typed-ast';
import { AST } from '../ast/ast';
import { ASTMapper } from '../ast/mapper';
import { requireNonNullish } from '../export';
import { createImport } from '../utils/importFuncGenerator';
import { INTO, U256_FROM_FELTS, U128_FROM_FELT, CONTRACT_ADDRESS } from '../utils/importPaths';
import {
INTO,
U256_FROM_FELTS,
U128_FROM_FELT,
CONTRACT_ADDRESS,
CUTOFF_DOWNCAST,
WARPLIB_INTEGER,
} from '../utils/importPaths';
import { safeGetNodeType } from '../utils/nodeTypeProcessing';
import { getContainingSourceUnit, primitiveTypeToCairo } from '../utils/utils';

Expand Down Expand Up @@ -41,6 +50,17 @@ export class CairoUtilImporter extends ASTMapper {
}
}

visitFunctionCall(node: FunctionCall, ast: AST): void {
// FIXME: remove when BitAnd is available for integer types in
// corelib
if (node.vFunctionName === CUTOFF_DOWNCAST[1]) {
const path = WARPLIB_INTEGER.slice(0, -1);
const name = requireNonNullish(WARPLIB_INTEGER.at(-1));
createImport(path, name, this.dummySourceUnit ?? node, ast);
}
super.visitFunctionCall(node, ast);
}

visitLiteral(node: Literal, ast: AST): void {
const type = safeGetNodeType(node, ast.inference);
if (type instanceof IntType && type.nBits > 251) {
Expand Down
10 changes: 10 additions & 0 deletions src/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,13 @@ export interface ExecSyncError {
export function instanceOfExecSyncError(object: any): object is ExecSyncError {
return 'stderr' in object;
}

export function requireNonNullish<T>(x: T | null | undefined): T {
if (x === null) {
throw new Error('Unexpected null');
}
if (x === undefined) {
throw new Error('Unexpected undefined');
}
return x as T;
}
1 change: 1 addition & 0 deletions src/utils/importFuncGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const FUNCTION_IMPORTS = [
Paths.U240_TO_FELT,
Paths.U248_TO_FELT,
Paths.U256_FROM_FELTS,
Paths.UPCAST,
Paths.ARRAY,
Paths.ARRAY_TRAIT,
Paths.BOOL_INTO_FELT252,
Expand Down
4 changes: 4 additions & 0 deletions src/utils/importPaths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,7 @@ export const WM_CREATE: [string[], string] = [ACCESSOR_TRAIT, 'warp_memory.creat
export const WM_TO_FELT_ARRAY: [string[], string] = [['not set yet'], 'wm_to_felt_array'];

export const SUPER: string[] = ['super'];

export const SIGNED_UPCAST: [string[], string] = [INTEGER_CONVERSIONS, 'signed_upcast'];
export const UPCAST: [string[], string] = [['integer'], 'upcast'];
export const CUTOFF_DOWNCAST: [string[], string] = [INTEGER_CONVERSIONS, 'cutoff_downcast'];
12 changes: 5 additions & 7 deletions src/warplib/generateWarplib.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { generateFile, PATH_TO_WARPLIB, WarplibFunctionInfo } from './utils';
import { int_conversions } from './implementations/conversions/int';
import { add, add_unsafe, add_signed, add_signed_unsafe } from './implementations/maths/add';
import { div_signed, div_signed_unsafe } from './implementations/maths/div';
import { exp, exp_signed, exp_signed_unsafe, exp_unsafe } from './implementations/maths/exp';
Expand Down Expand Up @@ -66,24 +65,23 @@ const mathWarplibFunctions: WarplibFunctionInfo[] = [
// bitwise_or - handwritten
bitwise_not(),
];
const conversionWarplibFunctions: WarplibFunctionInfo[] = [int_conversions()];
const inputCheckWarplibFunctions: WarplibFunctionInfo[] = [
external_input_check_ints(),
// external_input_check_address - handwritten
];
const warplibTypes: WarplibFunctionInfo[] = [fixed_bytes_types()];
const warp_memory: WarplibFunctionInfo[] = [warp_memory_fixed_bytes()];

generateWarplibFor('maths', mathWarplibFunctions, true);
generateWarplibFor('conversions', conversionWarplibFunctions, true);
generateWarplibFor('external_input_check', inputCheckWarplibFunctions, true);
generateWarplibFor('types', warplibTypes, true);
generateWarplibFor('maths', mathWarplibFunctions);
generateWarplibFor('conversions', []);
generateWarplibFor('external_input_check', inputCheckWarplibFunctions);
generateWarplibFor('types', warplibTypes);
generateWarplibFor('warp_memory', warp_memory, false);

function generateWarplibFor(
folderName: string,
functions: WarplibFunctionInfo[],
writeExportFile: boolean,
writeExportFile = true,
) {
functions.forEach((warpFunc: WarplibFunctionInfo) => generateFile(warpFunc, folderName));
if (writeExportFile) {
Expand Down
126 changes: 20 additions & 106 deletions src/warplib/implementations/conversions/int.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,12 @@
import assert from 'assert';
import { FunctionCall, generalizeType, IntType } from 'solc-typed-ast';
import { AST } from '../../../ast/ast';
import { createCallToFunction, createNumberTypeName, requireNonNullish } from '../../../export';
import { printNode, printTypeNode } from '../../../utils/astPrinter';
import { CUTOFF_DOWNCAST, SIGNED_UPCAST, UPCAST } from '../../../utils/importPaths';
import { safeGetNodeType } from '../../../utils/nodeTypeProcessing';
import {
bound,
forAllWidths,
IntFunction,
mask,
msb,
uint256,
WarplibFunctionInfo,
} from '../../utils';

export function int_conversions(): WarplibFunctionInfo {
return {
fileName: 'int_conversions',
imports: [
'from starkware.cairo.common.bitwise import bitwise_and',
'from starkware.cairo.common.cairo_builtins import BitwiseBuiltin',
'from starkware.cairo.common.math import split_felt',
'from starkware.cairo.common.uint256 import Uint256, uint256_add',
],
functions: [
...forAllWidths((from) => {
const x = forAllWidths((to) => {
if (from < to) {
if (to === 256) {
return [
`func warp_int${from}_to_int256{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(op : felt) -> (res : Uint256){`,
` let (msb) = bitwise_and(op, ${msb(from)});`,
` let (high, low) = split_felt(op);`,
` let naiveExtension = Uint256(low, high);`,
` if (msb == 0){`,
` return (naiveExtension,);`,
` }else{`,
` let (res, _) = uint256_add(naiveExtension, ${uint256(
sign_extend_value(from, to),
)});`,
` return (res,);`,
` }`,
'}',
];
} else {
return [
`func warp_int${from}_to_int${to}{bitwise_ptr: BitwiseBuiltin*}(op : felt) -> (res : felt){`,
` let (msb) = bitwise_and(op, ${msb(from)});`,
` if (msb == 0){`,
` return (op,);`,
` }else{`,
` return (op + 0x${sign_extend_value(from, to).toString(16)},);`,
` }`,
'}',
];
}
} else if (from === to) {
return [];
} else {
if (from === 256) {
if (to > 128) {
return [
`func warp_int${from}_to_int${to}{bitwise_ptr: BitwiseBuiltin*}(op : Uint256) -> (res : felt){`,
` let (high) = bitwise_and(op.high,${mask(to - 128)});`,
` return (op.low + ${bound(128)} * high,);`,
`}`,
];
} else {
return [
`func warp_int${from}_to_int${to}{bitwise_ptr: BitwiseBuiltin*}(op : Uint256) -> (res : felt){`,
` let (res) = bitwise_and(op.low, ${mask(to)});`,
` return (res,);`,
`}`,
];
}
} else {
return [
`func warp_int${from}_to_int${to}{bitwise_ptr : BitwiseBuiltin*}(op : felt) -> (res : felt){`,
` let (res) = bitwise_and(op, ${mask(to)});`,
` return (res,);`,
`}`,
];
}
}
});
return x.map((f) => f.join('\n')).join('\n');
}),
[
'func warp_uint256{range_check_ptr}(op : felt) -> (res : Uint256){',
' let split = split_felt(op);',
' return (Uint256(low=split.low, high=split.high),);',
'}',
].join('\n'),
],
};
}

function sign_extend_value(from: number, to: number): bigint {
return 2n ** BigInt(to) - 2n ** BigInt(from);
}
const cairoWidths = [8, 16, 32, 64, 128, 256];

export function functionaliseIntConversion(conversion: FunctionCall, ast: AST): void {
const arg = conversion.vArguments[0];
Expand All @@ -116,19 +25,24 @@ export function functionaliseIntConversion(conversion: FunctionCall, ast: AST):
)}`,
);

if (fromType.nBits < 256 && toType.nBits === 256 && !fromType.signed && !toType.signed) {
IntFunction(conversion, conversion.vArguments[0], 'uint', 'int_conversions', ast);
return;
} else if (
fromType.nBits === toType.nBits ||
(fromType.nBits < toType.nBits && !fromType.signed && !toType.signed)
) {
arg.typeString = conversion.typeString;
ast.replaceNode(conversion, arg);
const fromCairoWidth = requireNonNullish(cairoWidths.find((x) => x >= fromType.nBits));
const toCairoWidth = requireNonNullish(cairoWidths.find((x) => x >= toType.nBits));

if (fromCairoWidth === toCairoWidth) {
return;
}
let functionPath: [string[], string];
if (fromCairoWidth < toCairoWidth) {
functionPath = fromType.signed ? SIGNED_UPCAST : UPCAST;
} else {
const name = `${fromType.pp().startsWith('u') ? fromType.pp().slice(1) : fromType.pp()}_to_int`;
IntFunction(conversion, conversion.vArguments[0], name, 'int_conversions', ast);
return;
functionPath = CUTOFF_DOWNCAST;
}
const functionDef = ast.registerImport(
conversion,
...functionPath,
[['from', createNumberTypeName(fromType.nBits, fromType.signed, ast)]],
[['to', createNumberTypeName(toType.nBits, toType.signed, ast)]],
);
const functionCall = createCallToFunction(functionDef, conversion.vArguments, ast);
ast.replaceNode(conversion, functionCall);
}
1 change: 1 addition & 0 deletions tests/compilation/compilation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ const expectedResults = new Map<string, ResultType>(
['tryCatch.sol', 'WillNotSupport'],
['tupleAssignment7.sol', 'Success'],
['tupleAssignment8.sol', 'SolCompileFailed'],
['typeConversion/explicitIntConversion.sol', 'Success'],
['typeConversion/explicitTypeConversion.sol', 'Success'],
['typeConversion/implicitReturnConversion.sol', 'Success'],
['typeConversion/implicitTypeConv.sol', 'Success'],
Expand Down
14 changes: 9 additions & 5 deletions tests/compilation/contracts/address/7/256Address.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ pragma solidity ^0.7.6;

contract WARP {

function test160(uint160 me) public view {
address x = address(uint256(me));
function test160(uint160 n1, bytes20 n2) public view {
address a1 = address(uint256(n1));
address a2 = address(bytes32(n2));
}

function test256(uint256 me) public view {
address x = address(me);
uint256 y = uint256(x);
function test256(uint256 n1, bytes32 n2) public view {
address a1 = address(n1);
address a2 = address(n2);

uint256 m1 = uint256(a1);
bytes32 m2 = bytes32(a2);
}
}
14 changes: 9 additions & 5 deletions tests/compilation/contracts/address/8/256Address.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ pragma solidity ^0.8.6;

contract WARP {

function test160(uint160 me) public view {
address x = address(uint256(me));
function test160(uint160 n1, bytes20 n2) public view {
address a1 = address(uint256(n1));
address a2 = address(bytes32(n2));
}

function test256(uint256 me) public view {
address x = address(me);
uint256 y = uint256(x);
function test256(uint256 n1, bytes32 n2) public view {
address a1 = address(n1);
address a2 = address(n2);

uint256 m1 = uint256(a1);
bytes32 m2 = bytes32(a2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pragma solidity ^0.7.6;

contract WARP {

function conversions() public pure {
uint8 x1 = 1;
uint16 x2 = uint16(x1);
int32 x3 = int32(x2);
int64 x4 = int64(x3);
uint128 x5 = uint128(x4);
uint64 x6 = uint64(x5);
int32 x7 = int32(x6);
int16 x8 = int16(x7);
uint8 x9 = uint8(x8);
}
}
1 change: 1 addition & 0 deletions tests/compilation/passingContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ export const passingContracts = [
// 'tests/compilation/contracts/tryCatch.sol',
// 'tests/compilation/contracts/tupleAssignment7.sol',
// 'tests/compilation/contracts/tupleAssignment8.sol',
'tests/compilation/contracts/typeConversion/explicitIntConversion.sol',
'tests/compilation/contracts/typeConversion/explicitTypeConversion.sol',
// 'tests/compilation/contracts/typeConversion/implicitReturnConversion.sol',
// 'tests/compilation/contracts/typeConversion/implicitTypeConv.sol',
Expand Down
Loading

0 comments on commit 46821d3

Please sign in to comment.