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

Commit

Permalink
Use boolean operations as a part of Cairo1 compiler (#963)
Browse files Browse the repository at this point in the history
* remove boolean warplib generated functions

* remove input check for bools

* remove short circuit to conditionals

* do not check 256 bits int inputs

* created CairoBool type in cairoTypeSystem.ts

* lint

* Revert function output check

* include BoolType in imports

* Keep warp signed int comparison

* remove BoolxBool that was added on merge

* update literal writer for bools

* bijection between CairoBools and CairoFelts for memory representation

* minor conflict from merge with `felt` -> `felt252` syntax

* add conversions from felt to bools to warplib

* update storageWrite to use conversion trait

* use into trait in storageRead and support serialize read for bools

* lint and solve serializeRead on memoryRead

* minor error adding functions called

* convert to bool inisde serialize read

* lint

* use serde for bool serialization

* lint

* fix functionsCalled from merge in `storageWrite`

* new line
  • Loading branch information
AlejandroLabourdette authored Apr 24, 2023
1 parent f17f978 commit 2a911fa
Show file tree
Hide file tree
Showing 20 changed files with 100 additions and 146 deletions.
16 changes: 12 additions & 4 deletions src/cairoUtilFuncGen/serialisation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
CairoBool,
CairoFelt,
CairoStaticArray,
CairoStruct,
Expand All @@ -8,38 +9,45 @@ import {
WarpLocation,
} from '../utils/cairoTypeSystem';
import { TranspileFailedError } from '../utils/errors';
import { FELT252_INTO_BOOL } from '../utils/importPaths';

export function serialiseReads(
type: CairoType,
readFelt: (offset: number) => string,
readId: (offset: number) => string,
): [reads: string[], pack: string] {
): [reads: string[], pack: string, requiredImports: [string[], string][]] {
const packExpression = producePackExpression(type);
const reads: string[] = [];
const requiredImports: [string[], string][] = [];
const packString: string = packExpression
.map((elem: string | Read) => {
if (elem === Read.Felt) {
reads.push(readFelt(reads.length));
return `read${reads.length - 1}`;
} else if (elem === Read.Id) {
reads.push(readId(reads.length));
return `read${reads.length - 1}`;
} else if (elem === Read.Bool) {
requiredImports.push(FELT252_INTO_BOOL);
reads.push(readFelt(reads.length));
reads.push(`let read${reads.length} = felt252_into_bool(read${reads.length - 1});`);
} else {
return elem;
}
return `read${reads.length - 1}`;
})
.join('');
return [reads, packString];
return [reads, packString, requiredImports];
}

enum Read {
Felt,
Id,
Bool,
}

function producePackExpression(type: CairoType): (string | Read)[] {
if (type instanceof WarpLocation) return [Read.Id];
if (type instanceof CairoFelt) return [Read.Felt];
if (type instanceof CairoBool) return [Read.Bool];
if (type instanceof CairoStaticArray) {
return [
'(',
Expand Down
13 changes: 11 additions & 2 deletions src/cairoUtilFuncGen/storage/storageRead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
FunctionStateMutability,
TypeNode,
TypeName,
FunctionDefinition,
} from 'solc-typed-ast';
import { typeNameFromTypeNode } from '../../export';
import { CairoType, TypeConversionContext } from '../../utils/cairoTypeSystem';
Expand Down Expand Up @@ -59,9 +60,17 @@ export class StorageReadGen extends StringIndexedFuncGen {
}

private getOrCreate(typeToRead: CairoType): GeneratedFunctionInfo {
const functionsCalled: FunctionDefinition[] = [];
const funcName = `WS${this.generatedFunctionsDef.size}_READ_${typeToRead.typeName}`;
const resultCairoType = typeToRead.toString();
const [reads, pack] = serialiseReads(typeToRead, readFelt, readId);

const [reads, pack, requiredImports] = serialiseReads(typeToRead, readFelt, readId);

requiredImports.map((i) => {
const funcDef = this.requireImport(...i);
if (!functionsCalled.includes(funcDef)) functionsCalled.push(funcDef);
});

const funcInfo: GeneratedFunctionInfo = {
name: funcName,
code: endent`
Expand All @@ -70,7 +79,7 @@ export class StorageReadGen extends StringIndexedFuncGen {
${pack}
}
`,
functionsCalled: [],
functionsCalled: functionsCalled,
};
return funcInfo;
}
Expand Down
29 changes: 22 additions & 7 deletions src/cairoUtilFuncGen/storage/storageWrite.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import endent from 'endent';
import { Expression, FunctionCall, TypeNode, DataLocation, PointerType } from 'solc-typed-ast';
import {
Expression,
FunctionCall,
TypeNode,
DataLocation,
PointerType,
FunctionDefinition,
} from 'solc-typed-ast';
import {
CairoBool,
CairoType,
CairoUint,
CairoUint256,
TypeConversionContext,
} from '../../utils/cairoTypeSystem';
import { cloneASTNode } from '../../utils/cloning';
import { createCairoGeneratedFunction, createCallToFunction } from '../../utils/functionGeneration';
import { U128_TO_FELT } from '../../utils/importPaths';
import { BOOL_INTO_FELT252, U128_TO_FELT } from '../../utils/importPaths';
import { safeGetNodeType } from '../../utils/nodeTypeProcessing';
import { typeNameFromTypeNode } from '../../utils/utils';
import { add, GeneratedFunctionInfo, StringIndexedFuncGen } from '../base';
Expand Down Expand Up @@ -55,28 +63,35 @@ export class StorageWriteGen extends StringIndexedFuncGen {
}

private getOrCreate(typeToWrite: TypeNode): GeneratedFunctionInfo {
const functionsCalled: FunctionDefinition[] = [];
const cairoTypeToWrite = CairoType.fromSol(
typeToWrite,
this.ast,
TypeConversionContext.StorageAllocation,
);
const cairoTypeString = cairoTypeToWrite.toString();
const fnsToImport: [string[], string][] = [];
const writeCode = cairoTypeToWrite
.serialiseMembers('value')
.map((name, index) => {
if (cairoTypeToWrite instanceof CairoBool) {
functionsCalled.push(this.requireImport(...BOOL_INTO_FELT252));
return endent`
let intEncoded${index} = bool_into_felt252(${name});
${write(add('loc', index), `intEncoded${index}`)}
`;
}
if (cairoTypeToWrite.fullStringRepresentation === CairoUint256.fullStringRepresentation) {
functionsCalled.push(this.requireImport(...U128_TO_FELT));
name = `u128_to_felt252(${name})`;
fnsToImport.push(U128_TO_FELT);
} else if (cairoTypeToWrite instanceof CairoUint) {
name = `${cairoTypeString}_to_felt252(${name})`;
fnsToImport.push(toFeltfromuXImport(cairoTypeToWrite));
functionsCalled.push(this.requireImport(...toFeltfromuXImport(cairoTypeToWrite)));
}
return ` ${write(add('loc', index), name)}`;
})
.join('\n');

const funcName = `WS_WRITE${this.generatedFunctionsDef.size}`;
const funcName = `WS${this.generatedFunctionsDef.size}_WRITE_${cairoTypeToWrite.typeName}`;
const funcInfo: GeneratedFunctionInfo = {
name: funcName,
code: endent`
Expand All @@ -85,7 +100,7 @@ export class StorageWriteGen extends StringIndexedFuncGen {
return value;
}
`,
functionsCalled: [...fnsToImport.map((imp) => this.requireImport(...imp))],
functionsCalled: functionsCalled,
};
return funcInfo;
}
Expand Down
2 changes: 1 addition & 1 deletion src/cairoWriter/writers/literalWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class LiteralWriter extends CairoASTNodeWriter {
}

case LiteralKind.Bool:
return [node.value === 'true' ? '1' : '0'];
return [node.value];
case LiteralKind.String:
case LiteralKind.UnicodeString: {
if (
Expand Down
19 changes: 11 additions & 8 deletions src/passes/builtinHandler/mathsOperationToFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ import { createCallToFunction } from '../../utils/functionGeneration';
import { WARPLIB_MATHS } from '../../utils/importPaths';
import { createNumberLiteral, createUint256TypeName } from '../../utils/nodeTemplates';
import { functionaliseAdd } from '../../warplib/implementations/maths/add';
import { functionaliseAnd } from '../../warplib/implementations/maths/and';
import { functionaliseBitwiseAnd } from '../../warplib/implementations/maths/bitwiseAnd';
import { functionaliseBitwiseNot } from '../../warplib/implementations/maths/bitwiseNot';
import { functionaliseBitwiseOr } from '../../warplib/implementations/maths/bitwiseOr';
import { functionaliseDiv } from '../../warplib/implementations/maths/div';
import { functionaliseEq } from '../../warplib/implementations/maths/eq';
import { functionaliseExp } from '../../warplib/implementations/maths/exp';
import { functionaliseGe } from '../../warplib/implementations/maths/ge';
import { functionaliseGt } from '../../warplib/implementations/maths/gt';
Expand All @@ -26,8 +24,6 @@ import { functionaliseLt } from '../../warplib/implementations/maths/lt';
import { functionaliseMod } from '../../warplib/implementations/maths/mod';
import { functionaliseMul } from '../../warplib/implementations/maths/mul';
import { functionaliseNegate } from '../../warplib/implementations/maths/negate';
import { functionaliseNeq } from '../../warplib/implementations/maths/neq';
import { functionaliseOr } from '../../warplib/implementations/maths/or';
import { functionaliseShl } from '../../warplib/implementations/maths/shl';
import { functionaliseShr } from '../../warplib/implementations/maths/shr';
import { functionaliseSub } from '../../warplib/implementations/maths/sub';
Expand All @@ -45,26 +41,33 @@ export class MathsOperationToFunction extends ASTMapper {

visitBinaryOperation(node: BinaryOperation, ast: AST): void {
this.commonVisit(node, ast);
/* eslint-disable @typescript-eslint/no-empty-function */
// TODO: Let's disable for now this lint report in the file. The other functions should be reviewed when
// we do the bijection between Cairo1(uN) and Solidity(uintN). After that, the logic can be changed.
const operatorMap: Map<string, () => void> = new Map([
// Arith
['+', () => functionaliseAdd(node, this.inUncheckedBlock, ast)],
['-', () => functionaliseSub(node, this.inUncheckedBlock, ast)],
['*', () => functionaliseMul(node, this.inUncheckedBlock, ast)],
['/', () => functionaliseDiv(node, this.inUncheckedBlock, ast)],
['%', () => functionaliseMod(node, ast)],
['**', () => functionaliseExp(node, this.inUncheckedBlock, ast)],
['==', () => functionaliseEq(node, ast)],
['!=', () => functionaliseNeq(node, ast)],
// Comparison
['==', () => {}],
['!=', () => {}],
['>=', () => functionaliseGe(node, ast)],
['>', () => functionaliseGt(node, ast)],
['<=', () => functionaliseLe(node, ast)],
['<', () => functionaliseLt(node, ast)],
// Bitwise
['&', () => functionaliseBitwiseAnd(node, ast)],
['|', () => functionaliseBitwiseOr(node, ast)],
['^', () => functionaliseXor(node, ast)],
['<<', () => functionaliseShl(node, ast)],
['>>', () => functionaliseShr(node, ast)],
['&&', () => functionaliseAnd(node, ast)],
['||', () => functionaliseOr(node, ast)],
// Logic
['&&', () => {}],
['||', () => {}],
]);

const thunk = operatorMap.get(node.operator);
Expand Down
4 changes: 0 additions & 4 deletions src/passes/conditionalSplitter/conditionalSplitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,6 @@ export class ConditionalSplitter extends ExpressionSplitter {
// Function to add passes that should have been run before this pass
addInitialPassPrerequisites(): void {
const passKeys: Set<string> = new Set<string>([
// Short circuiting of and/or expressions must be handled before
// extracting expressions with this splitter, otherwise both
// expressions (left and right in that operation) might get evaluated.
'Sc',
// Assignments operator is assumed to be '=', so the other possible
// operators like '+=' or '*=' need to be handled first, which is done
// in UnloadingAssignment pass
Expand Down
2 changes: 0 additions & 2 deletions src/passes/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export * from './dropUnusedSourceUnit';
export * from './enumConverter';
export * from './expressionSplitter';
export * from './conditionalSplitter/export';
export * from './shortCircuitToConditional';
export * from './freeFunctionInliner';
export * from './externalArgModifier/export';
export * from './externalContractHandler/export';
Expand Down Expand Up @@ -41,7 +40,6 @@ export * from './rejectUnsupportedFeatures';
export * from './replaceIdentifierContractMemberAccess';
export * from './returnInserter';
export * from './returnVariableInitializer';
export * from './shortCircuitToConditional';
export * from './sourceUnitSplitter';
export * from './staticArrayIndexer';
export * from './storageAllocator';
Expand Down
1 change: 0 additions & 1 deletion src/passes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export * from './rejectPrefix';
export * from './replaceIdentifierContractMemberAccess';
export * from './returnInserter';
export * from './returnVariableInitializer';
export * from './shortCircuitToConditional';
export * from './sourceUnitSplitter';
export * from './staticArrayIndexer';
export * from './storageAllocator';
Expand Down
36 changes: 0 additions & 36 deletions src/passes/shortCircuitToConditional.ts

This file was deleted.

2 changes: 0 additions & 2 deletions src/transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import {
Require,
ReturnInserter,
ReturnVariableInitializer,
ShortCircuitToConditional,
SourceUnitPathFixer,
SourceUnitSplitter,
StaticArrayIndexer,
Expand Down Expand Up @@ -129,7 +128,6 @@ function applyPasses(
['R', ReturnInserter],
['Rv', ReturnVariableInitializer],
['Ifr', IdentityFunctionRemover],
['Sc', ShortCircuitToConditional],
['U', UnloadingAssignment],
['Cos', ConditionalSplitter],
['V', VariableDeclarationInitialiser],
Expand Down
17 changes: 16 additions & 1 deletion src/utils/cairoTypeSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export abstract class CairoType {
return new CairoStaticArray(elementType, narrowedLength);
}
} else if (tp instanceof BoolType) {
return new CairoFelt();
return new CairoBool();
} else if (tp instanceof BuiltinType) {
throw new NotSupportedYetError('Serialising BuiltinType not supported yet');
} else if (tp instanceof BuiltinStructType) {
Expand Down Expand Up @@ -169,6 +169,21 @@ export class CairoFelt extends CairoType {
}
}

export class CairoBool extends CairoType {
get fullStringRepresentation(): string {
return '[Bool]';
}
toString(): string {
return 'bool';
}
get width(): number {
return 1;
}
serialiseMembers(name: string): string[] {
return [name];
}
}

export class CairoUint extends CairoType {
constructor(public nBits: number = 256) {
super();
Expand Down
2 changes: 2 additions & 0 deletions src/utils/importFuncGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ export function createImport(
Paths.ARRAY_TRAIT,
Paths.WARP_MEMORY,
Paths.MEMORY_TRAIT,
Paths.BOOL_INTO_FELT252,
Paths.FELT252_INTO_BOOL,
];
if (pathsForFunctionImport.some((i) => encodePath([path, name]) === encodePath(i))) {
return createFuncImport();
Expand Down
5 changes: 4 additions & 1 deletion src/utils/importPaths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const GET_CONTRACT_ADDRESS: [string[], string] = [
export const WARPLIB_MEMORY = ['warplib', 'memory'];
export const WARPLIB_KECCAK = ['warplib', 'keccak'];
export const WARPLIB_MATHS = ['warplib', 'maths'];
export const WARPLIB_INTEGER = ['warplib', 'integer'];

export const DYNAMIC_ARRAYS_UTIL = ['warplib', 'dynamic_arrays_util'];
export const BYTES_CONVERSIONS = [...WARPLIB_MATHS, 'bytes_conversions'];
Expand Down Expand Up @@ -93,7 +94,9 @@ export const WM_ALLOC: [string[], string] = [[...WARPLIB_MEMORY], 'wm_alloc'];
export const WM_WRITE_FELT: [string[], string] = [[...WARPLIB_MEMORY], 'wm_write_felt'];
export const ARRAY: [string[], string] = [['array'], 'Array'];
export const ARRAY_TRAIT: [string[], string] = [['array'], 'ArrayTrait'];
export const U256_FROM_FELTS: [string[], string] = [['warplib', 'integer'], 'u256_from_felts'];
export const U256_FROM_FELTS: [string[], string] = [[...WARPLIB_INTEGER], 'u256_from_felts'];
export const FELT252_INTO_BOOL: [string[], string] = [[...WARPLIB_INTEGER], 'felt252_into_bool'];
export const BOOL_INTO_FELT252: [string[], string] = [[...WARPLIB_INTEGER], 'bool_into_felt252'];

/** cairo1 uX <-> felt conversions */

Expand Down
3 changes: 1 addition & 2 deletions src/utils/nodeTypeProcessing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,7 @@ export function checkableType(type: TypeNode): boolean {
type instanceof FixedBytesType ||
type instanceof UserDefinedType ||
type instanceof AddressType ||
type instanceof IntType ||
type instanceof BoolType ||
(type instanceof IntType && type.nBits < 256) ||
type instanceof StringType
);
}
Expand Down
7 changes: 0 additions & 7 deletions src/warplib/implementations/maths/and.ts

This file was deleted.

Loading

0 comments on commit 2a911fa

Please sign in to comment.