Skip to content
This repository has been archived by the owner on May 11, 2020. It is now read-only.

Part 1/2 #101 - implement partial compilation to native code #105

Closed
wants to merge 21 commits into from
Closed

Part 1/2 #101 - implement partial compilation to native code #105

wants to merge 21 commits into from

Conversation

twitchyliquid64
Copy link
Contributor

This PR lays the boilerplate for supporting AOT & JIT compilation.

  • Use one of the unassigned opcodes to signal to the interpreter to execute a native code section. Modify the disassembler to throw an error if this opcode is used, so any collision years down the line is detected & can be trivially fixed.

  • Implement interfaces to abstract different stages, specifically:

    1. Scanning opcodes to determine candidates for native compilation + gathering statistics (sequenceScanner)
    2. Allocating executable pages, as such an operation is OS dependent (pageAllocator)
    3. Actually compiling the chosen sequence into instructions (instructionBuilder)
  • When a sequence of opcodes is rewritten, the beginning is set to an internal opcode that signals the interpreter should run a section of native code at a specific []asmBlock index. Any remaining opcodes are set to ops.Unreachable - as we dont plan to emit asm where there are inbound branches in the middle (yet), this should crash out in the event of any bugs that jump into the recompiled section.

  • Tests for integration between the different interfaces & structures in VM / compiledFunction.

@codecov-io
Copy link

codecov-io commented Mar 10, 2019

Codecov Report

Merging #105 into master will decrease coverage by 3.14%.
The diff coverage is 63.02%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #105      +/-   ##
==========================================
- Coverage    68.9%   65.76%   -3.15%     
==========================================
  Files          33       40       +7     
  Lines        3345     3867     +522     
==========================================
+ Hits         2305     2543     +238     
- Misses        783     1058     +275     
- Partials      257      266       +9
Impacted Files Coverage Δ
exec/internal/compile/compile.go 0% <0%> (ø)
exec/internal/compile/scanner.go 0% <0%> (ø)
wasm/operators/op.go 75% <0%> (ø) ⬆️
exec/func.go 76% <100%> (+0.48%) ⬆️
exec/internal/compile/native_exec.go 100% <100%> (ø)
exec/native_compile_nogae.go 100% <100%> (ø)
exec/native_compile.go 65.9% <65.9%> (ø)
exec/vm.go 85.01% <71.42%> (-1.76%) ⬇️
exec/internal/compile/amd64.go 79.5% <79.5%> (ø)
exec/internal/compile/allocator.go 81.81% <81.81%> (ø)
... and 7 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a76146c...b8b6d63. Read the comment docs.

Tom added 3 commits March 9, 2019 21:24
…compile/native, track instruction metadata when emitting internal bytecode
@twitchyliquid64
Copy link
Contributor Author

On second thought, hold off on reviewing this. I need to think about a better way to structure this.

@twitchyliquid64
Copy link
Contributor Author

@sbinet, can you look at this and give me your thoughts? I'm not done, but getting close. This PR is mostly boilerplate, but usefully implements AOT compilation for i64.const, i64.add, & i64.sub.

  • Changes to the disasm package to track the encoded size of each instruction it reads. I'm not using this anymore, but the code + tests are written. Let me know if you want me to leave it in or delete the code.

  • Changes to exec/exec_test.go to run each test suite twice - once with AOT compilation disabled & once enabled, all in parallel.

  • Changes to exec/internal/compile/compile.go to track instruction metadata and jump targets. Compile() now additionally returns a compile.BytecodeMetadata object to track information about the compiled bytecode. I use it a little now (mostly to avoid the overhead I would otherwise need to disassemble again), but it will be really invaluable for when we implement a JIT (as opposed to just AOT).

  • Changes to exec/func.go: A slice of emitted native code sections is kept for each function (compiledFunction.asm).

  • exec/internal/compile/allocator.go: Implements a naive executable page allocator. I know this is very leaky for the moment.

  • exec/internal/compile/amd64.go: Implements the backend for the amd64 architecture. This is very simplistic ATM and only supports 3 opcodes, but this is a good start. There are lots of easy optimizations we can make here too - tracking if a register already has stack information or what would be pushed to the stack is a good start. Peephole optimizations and dead-instruction deletion would also be great too.

  • exec/internal/compile/amd64_test.go: Test suite for the amd64 backend. Of particular note is the TestSliceMemoryLayoutAMD64 function, which confirms that the memory layout of the slice (which is critical for the emitted instructions to work).

  • exec/internal/compile/scanner.go: Implements a generic scanner which any backend can use. This calculates heuristics about the instructions (used for deciding whether to native-compile them or not, not very useful now but will be for a JIT) as well as working out which subsets have only instructions which are fully supported by the backend.

  • exec/native_compile.go: Abstracts access to the native backend, as well as having the main logic for AOT compilation & actually executing native code.

  • wasm/operators/wagon_internal.go: I've added a new interpreter opcode to symbolize execution of a sequence of native code.

Copy link
Contributor

@sbinet sbinet left a comment

Choose a reason for hiding this comment

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

still digesting... (just a first pass.)

disasm/disasm.go Outdated Show resolved Hide resolved
exec/internal/compile/allocator.go Show resolved Hide resolved
exec/internal/compile/allocator_test.go Show resolved Hide resolved
exec/internal/compile/amd64.go Show resolved Hide resolved
exec/internal/compile/amd64_test.go Show resolved Hide resolved
exec/internal/compile/scanner.go Show resolved Hide resolved
exec/native_compile.go Show resolved Hide resolved
exec/native_compile_test.go Show resolved Hide resolved
wasm/operators/wagon_internal.go Show resolved Hide resolved
exec/func.go Outdated Show resolved Hide resolved
@twitchyliquid64
Copy link
Contributor Author

Ive addressed the comments, PTAL.

@twitchyliquid64
Copy link
Contributor Author

Friendly ping @sbinet :)

Copy link
Contributor

@sbinet sbinet left a comment

Choose a reason for hiding this comment

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

still digging through :)

exec/internal/compile/amd64.go Show resolved Hide resolved
exec/internal/compile/amd64_test.go Show resolved Hide resolved
exec/internal/compile/amd64_test.go Outdated Show resolved Hide resolved
exec/internal/compile/amd64_test.go Outdated Show resolved Hide resolved
exec/internal/compile/amd64_test.go Outdated Show resolved Hide resolved
exec/native_compile_test.go Outdated Show resolved Hide resolved
exec/native_compile_test.go Outdated Show resolved Hide resolved
exec/vm.go Outdated Show resolved Hide resolved
wasm/operators/wagon_internal.go Outdated Show resolved Hide resolved
exec/vm.go Outdated Show resolved Hide resolved
@twitchyliquid64
Copy link
Contributor Author

Done :) PTAL.

…, off-by-one in asm emitter in some conditions, add custom test case which can be fully compiled, written in rust.
@twitchyliquid64
Copy link
Contributor Author

Apologies for the additional commit - I was intending for that to be a different PR, but I found a few bugs that shouldn't be left out of this PR (off-by-one in the return instruction pointer, double-allocation/overrun for vm.context.stack).

…+ relies on less implementation behaviour.
@twitchyliquid64
Copy link
Contributor Author

Friendly ping @sbinet :)

@twitchyliquid64
Copy link
Contributor Author

Let me know if there's anything I can do to move this along. If you would like me to split some of this into separate PRs, (like the scanner in one and the execution in another) I can try. Let me know :)

@sbinet
Copy link
Contributor

sbinet commented Mar 24, 2019

Apologies for the sluggish answers. (I am on the road to the dotGo conference...)

If you can split some PRs off, then yes, by all means: smaller bites are great :)

@twitchyliquid64
Copy link
Contributor Author

No worries at all :) I'm sorry for being pushy, just trying to get this in before my excitement shifts to other ideas.

I'll go ahead and split this into 2-3 different PRs, and close this one.

Thanks, and enjoy dotGo! Say hi to Dave for me 😄

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

Successfully merging this pull request may close these issues.

None yet

3 participants