-
Notifications
You must be signed in to change notification settings - Fork 1.8k
C++: Better IR for varargs #3092
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
C++: Better IR for varargs #3092
Conversation
This PR changes the IR we generate for functions that accept a variable argument list. Rather than simply using `BuiltInOperationInstruction` to model the various `va_*` macros as mysterious function-like operations, we now model them in more detail. The intent is to enable better alias analysis and taint flow through varargs. The `va_start` macro now generates a unary `VarArgsStart` instruction that takes the address of the ellipsis pseudo-parameter as its operand, and returns a value of type `std::va_list`. This value is then stored into the actual `std::va_list` variable via a regular `Store`. The `va_arg` macro now loads the `std::va_list` argument, then emits a `VarArg` instruction on the result. This returns the address of the vararg argument to be loaded. That address is later used as the address operand of a regular `Load` to return the value of the argument. To model the side effect of moving to the next argument, we emit a `NextVarArg` instruction that takes the previous `std::va_list` value and returns an updated one, which is then stored back into the `std::va_list` variable. The `va_end` macro just emits a `VarArgsEnd` unary instruction that takes the address of the `std::va_list` argument and does nothing, since `va_end` doesn't really do anything on most compiler implementations anyway. The `va_copy` macro is just modeled as a plain copy.
The The same considerations apply to In any case, please add (or point me to) a test where |
@jbj I thought I had the whole pointer-vs-array thing figured out, but clearly I should have added the test case you suggest, which would have shown me what I was missing. I'll add that test case and update the codegen accordingly. |
In the Unix ABI, `std::va_list` is defined as `typedef struct __va_list_tag { ... } va_list[1];`, which means that any `std::va_list` used as a function parameter decays to `struct __va_list_tag*`. Handling this actually made the QL code slightly cleaner. The only tricky bit is that we have to determine what type to use as the actual `va_list` type when loading, storing, or modifying a `std::va_list`. To do this, we look at the type of the argument to the `va_*` macro. A detailed QLDoc comment explains the details. I added a test case for passing a `va_list` as an argument, and then manipulating that `va_list` in the callee.
Fixing the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. The new TranslatedElement
s have a daunting amount of detail in them, but it looks mostly routine, and the test output looks as I'd expect.
This PR changes the IR we generate for functions that accept a variable argument list. Rather than simply using
BuiltInOperationInstruction
to model the variousva_*
macros as mysterious function-like operations, we now model them in more detail. The intent is to enable better alias analysis and taint flow through varargs.The
va_start
macro now generates a unaryVarArgsStart
instruction that takes the address of the ellipsis pseudo-parameter as its operand, and returns a value of typestd::va_list
. This value is then stored into the actualstd::va_list
variable via a regularStore
.The
va_arg
macro now loads thestd::va_list
argument, then emits aVarArg
instruction on the result. This returns the address of the vararg argument to be loaded. That address is later used as the address operand of a regularLoad
to return the value of the argument. To model the side effect of moving to the next argument, we emit aNextVarArg
instruction that takes the previousstd::va_list
value and returns an updated one, which is then stored back into thestd::va_list
variable.The
va_end
macro just emits aVarArgsEnd
unary instruction that takes the address of thestd::va_list
argument and does nothing, sinceva_end
doesn't really do anything on most compiler implementations anyway.The
va_copy
macro is just modeled as a plain copy.Next up: Vararg-aware alias analysis (at least enough to get us to the point where we can thread taint flow).