Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding label support to assembler #36

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

Conversation

lialan
Copy link

@lialan lialan commented Nov 16, 2019

I would like to use this little tool to do some easy assembling in my project but found that this does not support label syntax, such as:

    ...
some_label:
    JUMPDEST
    ...
    PUSH some_label
    JUMP

So I just added the labeling syntax.

Another thing is that it seems that EVM memory space address starts with 1 rather than 0. So I changed pc according.

@CLAassistant
Copy link

CLAassistant commented Nov 16, 2019

CLA assistant check
All committers have signed the CLA.

@@ -329,7 +328,17 @@ def is_arithmetic(self):
'ADD', 'MUL', 'SUB', 'DIV', 'SDIV', 'MOD', 'SMOD', 'ADDMOD', 'MULMOD', 'EXP', 'SIGNEXTEND', 'SHL', 'SHR', 'SAR'}


def assemble_one(asmcode, pc=0, fork=DEFAULT_FORK):
def is_push(instr):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternative instr.semantics == "PUSH"

Also if you really need an is_push make one like is_branch (you can use opcodes instead of semantics if you want)

pyevmasm/pyevmasm/evmasm.py

Lines 305 to 308 in 0933d39

@property
def is_branch(self):
""" True if the instruction is a jump """
return self.semantics in {'JUMP', 'JUMPI'}

return instr
except:
raise AssembleError("Something wrong at pc %d" % pc)

def fixup_instr(instr, label_offset):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Being it so simple I vote to do this inline so we do not need to document, maintain, etc and we save 1 func call.

line = line[:index]

# skip directives:
if line.find(".") is 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line.statrswith ?

pc += instr.size

# fixup instructions
for label in labels:
if label not in fillins.keys():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for keys(). label not in fillins is enough I think, no?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you can check if the instruction has any operan referring to a label and fix them.

Add an Instruction.uses_labels() that encapsulates that?

else:
fillins[operand] = [pc]
else:
instr.operand = int(asmcode[1], 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idea:

Not sure we need the fillings dict?
Maybe just write whatever there is to the instruction.operand. An int or label(string).
And then consider the instructions with a string operand to need fixup. ?

Instruction will not be able to generate a bytecode until it is "fixedup"

for label in labels:
if label not in fillins.keys():
continue
for instr in instrs:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of iterating over al linstructions for each label that qualifies maybe just rework all this do a single iteration over the instructions and fix the ones that needs it ? The instructions with Instruction.uses_labels() true

@feliam
Copy link
Contributor

feliam commented Nov 24, 2019

Hey @lialan thank you for this. The idea of having labels and other more high level constructs is very interesting. I was secretly expecting for someone to use this in some compiler-ish project.

I gave it a first pass.
We may be on the verge of needing a Contract class that contains the more high level stuff. (labels, directives)

I hope we do not loss the filter like structure of the disassembler/assembler. cat bytecode | disassemble | assemble
We'll see.

@lialan
Copy link
Author

lialan commented Nov 28, 2019

hi @feliam, thanks for the reply (and suggestions!), I will update accordingly to your comments later this week.

so I basically am using this assembler in my LLVM project for EVM backend, and I need an easy way to generate some LLVM IR for EVM target, such as https://github.com/etclabscore/evm_llvm/blob/EVM/test/CodeGen/EVM/runtime_tests/fib.ll

Because I have not figured out how to represent contract creation code in LLVM code so here is what I am doing: using a contract template, instantiate it with with generated EVM assembly, then emit a real contract assembly, then use this assembler to emit EVM binary for testing on Geth.

Here is how I integrate it into my testing workflow:
https://github.com/etclabscore/evm_llvm/blob/EVM/tools/evm-test/evm_test.py

Yes, this is a real use case of your code, kudo for that, and thanks for this little tool! I will try to see if I can figure out your comments and make proper changes to this pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants