Compiling an LLL Contract for the First Time

Daniel Ellison
ConsenSys Media
Published in
4 min readMay 15, 2017

This article will go through the steps necessary to compile an LLL-based smart contract, producing a binary that’s suitable for deployment to an Ethereum blockchain. It is a companion piece to my screencast on the same subject. The compilation process will be done at a bash command prompt. If there’s any interest I’ll cover the macOS and Windows processes as well.

Requirements

The first thing you’ll need is a compiler. If you followed my previous screencast or read its supporting article you’re all set. If not, you can either follow along here without trying to compile–which sort of defeats the purpose of this article–or you can go and install the Ethereum compilers and come back when you’re done.

To ensure that you have an LLL compiler available to you, execute the following in your terminal:

$ lllc --version
LLLC, the Lovely Little Language Compiler
Version: 0.4.12-develop.2017.5.4+commit.2d89cfaa.Linux.g++

If you don’t see a response similar to the above, you’ll need to figure out what’s wrong and fix it. Troubleshooting is beyond the scope of this article.

Getting started

Since this isn’t a tutorial on LLL syntax, I’ll present a contract with a function that simply returns a value passed in to it. This is known in mathematics as an identity function. A Solidity implementation would look like this:

pragma solidity ^0.4.0;
contract IdentityFunction {
function identity (uint value) returns (uint) {
return value;
}
}

Since we’re working at a lower level, we’ll have to be a bit more verbose when implementing this in LLL. I’ve defined a few macros at the start of the contract to make reading the code itself a little easier. You don’t need to understand how the macros work right now; there will be followup articles and screencasts explaining everything in detail.

Here is the LLL version of an identity function:

(seq
(def 'scratch 0x00)
(def 'identity 0xac37eebb)
(def 'function (function-hash code-body)
(when (= (div (calldataload 0x00) (exp 2 224)) function-hash)
code-body))
(returnlll
(function identity
(seq
(mstore scratch (calldataload 0x04))
(return scratch 32)))))

The identity function itself occurs after the line containing(function identity. It simply reads the passed in value from the call data, stores it in memory at 0x00, and then returns the value to the caller.

Compiling

Now that we have a contract that we can compile, we’re ready to hit the command line again. At the bash prompt, move to the directory in which you’ve saved the above source code. I’ll assume you’ve saved the source in your home directory and that the filename is tutorial.lll. Type the following at the terminal:

$ lllc --hex tutorial.lll
601f80600c6000396000f30063ac37eebb60e060020a600035041415601e5760043560005260206000f35b

You’ll see a long string of hexadecimal numbers. That’s the ascii representation of the LLL contract’s compiled bytecode. For an alternative view of the compiler’s output, type the following:

$ lllc --assembly tutorial.lll
dataSize(sub_0)
dup1
dataOffset(sub_0)
0x0
codecopy
0x0
return
stop
sub_0: assembly {
jumpi(tag_13, iszero(eq(div(calldataload(0x0), exp(0x2, 0xe0)), 0xac37eebb)))
mstore(0x0, calldataload(0x4))
return(0x0, 0x20)
tag_13:
}

This produces the assembly representation of the contract. There’s currently no way to compile the assembly source as there’s no standalone EVM assembler, though there’s work being done in that area. But with slight modifications, this could be plugged into an assembly {} block in a Solidity contract.

Comparison

Congratulations! You’ve just compiled your first contract in LLL. Few people can make that claim at the moment. For comparison, let’s follow the same sequence for the Solidity implementation. Again, I’ll assume you’re in your home directory and that the filename is tutorial.sol. Type this in your terminal:

$ solc --optimize --bin tutorial.sol
======= tutorial.sol:IdentityFunction =======
Binary:
60606040523415600b57fe5b5b608e8061001a6000396000f300606060405263ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663ac37eebb81146039575bfe5b3415604057fe5b6049600435605b565b60408051918252519081900360200190f35b805b9190505600a165627a7a723058207e9beec0ba448764b730a298f76a0bca158f54026795209c3df1fce6ce037e480029

This produces the ascii representation of the Solidity contract’s compiled bytecode. One thing you may notice is that despite the Solidity source being somewhat smaller, the resulting binary is significantly larger. The contract was even compiled using the optimizer; without that the binary would be even larger.

Let’s take a look at the assembly output of tutorial.sol. Type the following at the command line:

$ solc --optimize --asm tutorial.sol
======= tutorial.sol:IdentityFunction =======
EVM assembly:
/* "tutorial.sol":25:141 contract IdentityFunction {... */
mstore(0x40, 0x60)
jumpi(tag_1, iszero(callvalue))
invalid
tag_1:
tag_2:
dataSize(sub_0)
dup1
dataOffset(sub_0)
0x0
codecopy
0x0
return
stop
sub_0: assembly {
/* "tutorial.sol":25:141 contract IdentityFunction {... */
mstore(0x40, 0x60)
and(div(calldataload(0x0), 0x100000000000000000000000000000000000000000000000000000000), 0xffffffff)
0xac37eebb
dup2
eq
tag_2
jumpi
tag_1:
invalid
/* "tutorial.sol":55:139 function identity (uint256 input) constant returns (uint256) {... */
tag_2:
jumpi(tag_3, iszero(callvalue))
invalid
tag_3:
tag_4
calldataload(0x4)
jump(tag_5)
tag_4:
0x40
dup1
mload
swap2
dup3
mstore
mload
swap1
dup2
swap1
sub
0x20
add
swap1
return
tag_5:
/* "tutorial.sol":129:134 input */
dup1
/* "tutorial.sol":55:139 function identity (uint256 input) constant returns (uint256) {... */
tag_6:
swap2
swap1
pop
jump // out
}

You’ll note that the Solidity assembly output is significantly larger than the LLL output. That is one of the advantages of writing contracts in LLL: The binaries produced are smaller on the whole, which translates to a reduction in the gas necessary for deployment. With more complex contracts, there will also be reduced gas costs when invoking functions.

Conclusion

I hope this has given you a taste of what it’s like to compile an LLL contract, and an appreciation of the gas economy possible. I’ll be creating more articles and screencasts going into details on all aspects of LLL smart contract development.

Read More:

An Introduction to LLL for Ethereum Smart Contract Development
Building and Installing Ethereum Compilers

Deploying Your First LLL Contract — Part 1
Deploying Your First LLL Contract — Part 2
The Structure of an LLL Contract — Part 1
The Structure of an LLL Contract — Part 2
The Structure of an LLL Contract — Part 3

Like this piece? Sign up here for the ConsenSys weekly newsletter.

Disclaimer: The views expressed by the author above do not necessarily represent the views of Consensys AG. ConsenSys is a decentralized community with ConsenSys Media being a platform for members to freely express their diverse ideas and perspectives. To learn more about ConsenSys and Ethereum, please visit our website.

--

--