Skip to content

Conversation

@niaow
Copy link
Member

@niaow niaow commented Apr 10, 2020

Previously, the function value lowering pass had special cases for when there were 0 or 1 function implementations. However, the results of the pass were incorrect in both of these cases. This change removes the specializations and fixes the transformation.

In the case that there was a single function implementation, the compiler emitted a select instruction to obtain the function pointer. This selected between null and the implementing function pointer.
While this was technically correct, it failed to eliminate indirect function calls. This prevented discovery of these calls by the coroutine lowering pass, and caused async function calls to be passed through unlowered. As a result, the generated code had undefined behavior (usually resulting in a segfault).

In the case of no function implementations, the lowering code was correct. However, the lowering code was not run. The discovery of function signatures was accomplished by scanning implementations, and when there were no implementations nothing was discovered or lowered.

For maintainability reasons, I have removed both specializations rather than fixing them. This substantially simplifies the code, and reduces the amount of variation that we need to worry about for testing purposes. The IR now generated in the cases of 0 or 1 function implementations can be efficiently simplified by LLVM's optimization passes. Therefore, there should not be a substantial regression in terms of performance or machine code size.

I have also inspected some final output IR, and it seems that LLVM optimizes it to something similar to what we had wanted for a single function implementation:

  %switch = icmp eq i64 %.unpack2, 0
  br i1 %switch, label %func.nil, label %func.call1

@niaow
Copy link
Member Author

niaow commented Apr 10, 2020

This fixes #1032

@niaow niaow added this to the v0.13 milestone Apr 10, 2020
@aykevl
Copy link
Member

aykevl commented Apr 10, 2020

This caused a conflict, can you rebase on top of the dev branch to fix that?

@niaow niaow force-pushed the removesinglefunclowering branch from 4f06878 to ce773b1 Compare April 10, 2020 12:51
@niaow
Copy link
Member Author

niaow commented Apr 10, 2020

I finally managed to rebase.

… lowering and fix lowering of a function value of an unimplemented type

Previously, the function value lowering pass had special cases for when there were 0 or 1 function implementations.
However, the results of the pass were incorrect in both of these cases.
This change removes the specializations and fixes the transformation.

In the case that there was a single function implementation, the compiler emitted a select instruction to obtain the function pointer.
This selected between null and the implementing function pointer.
While this was technically correct, it failed to eliminate indirect function calls.
This prevented discovery of these calls by the coroutine lowering pass, and caused async function calls to be passed through unlowered.
As a result, the generated code had undefined behavior (usually resulting in a segfault).

In the case of no function implementations, the lowering code was correct.
However, the lowering code was not run.
The discovery of function signatures was accomplished by scanning implementations, and when there were no implementations nothing was discovered or lowered.

For maintainability reasons, I have removed both specializations rather than fixing them.
This substantially simplifies the code, and reduces the amount of variation that we need to worry about for testing purposes.
The IR now generated in the cases of 0 or 1 function implementations can be efficiently simplified by LLVM's optimization passes.
Therefore, there should not be a substantial regression in terms of performance or machine code size.
@niaow niaow requested a review from aykevl April 10, 2020 15:22
@aykevl
Copy link
Member

aykevl commented Apr 11, 2020

Tested the size and while it does affect code size on wasm (mainly), it is usually slightly reduced.
Will review the code soon.

@aykevl aykevl merged commit 9890c76 into tinygo-org:dev Apr 12, 2020
@aykevl
Copy link
Member

aykevl commented Apr 12, 2020

Thank you for fixing this bug!

uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)

// Find all func values used in the program with their signatures.
funcValueWithSignaturePtr := llvm.PointerType(mod.GetTypeByName("runtime.funcValueWithSignature"), 0)
Copy link
Member

Choose a reason for hiding this comment

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

Sidenote: this will eventually need to be changed. I discovered that types may be renamed when LLVM modules are merged (when there are duplicates). This is a big problem in the interface lowering pass and I'm not yet sure how to fix that.

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.

2 participants