Extending FunctionType

Hi all,

I am trying to extend a FunctionType to include new parameters. In particular, I want to
ensure that the main function has been declared with both argsc and argsv. However
there seems to be no easy way to accomplish this: llvm::Function::getFunctionType() returns a
a reference to a const object, while modifying only the argument list yields an error during verification
since the function type does not match its arguments. Is there any approach that I am missing or
a simple workaround to this problem?

Thanks in advance, and best regards,
Gabriel

If I understand correctly, you are trying to add a parameter to the main() function, correct?

If so, then you can’t just modify the existing main() function. Instead, you have to create a new function with an empty function body with the new parameter and then clone the body of the old main() function into the new main() function.

There is code to do that in the poolalloc project. Check out and look for the code that does this in lib/PoolAllocate/PoolAllocate.cpp in the MakeFunctionClone() method. – John T.

I don't think a full clone is necessary, since he wants to replace the
function. He only needs to create the new function and splice in the
body of the old one.

Gabriel: look at Function::getBasicBlockList() and
iplist<>::splice(iterator, iplist). Something like
  Function *NewF = Function::Create(NewFnType, OldF->getLinkage());
  NewF->getBasicBlockList().splice(NewF->begin(), OldF->getBasicBlockList());
  NewF->takeName(OldF);
  OldF->eraseFromParent();
is probably what you're looking for.
(Note: completely untested)

Hi all,

I am trying to extend a FunctionType to include new parameters. In
particular, I want to
ensure that the main function has been declared with both argsc and argsv.
However
there seems to be no easy way to accomplish this:
llvm::Function::getFunctionType() returns a
a reference to a const object, while modifying only the argument list yields
an error during verification
since the function type does not match its arguments. Is there any approach
that I am missing or
a simple workaround to this problem?

If I understand correctly, you are trying to add a parameter to the main()
function, correct?

If so, then you can't just modify the existing main() function. Instead,
you have to create a new function with an empty function body with the new
parameter and then clone the body of the old main() function into the new
main() function.

I don't think a full clone is necessary, since he wants to replace the
function. He only needs to create the new function and splice in the
body of the old one.

That is exactly what MakeFunctionClone() does. It creates a new function and uses CloneFunctionInto() to create a copy of the instructions in the old function in the new function. The old function can be removed afterward if desired.

I'm not sure if the code below would work. I don't see a mechanism that updates instructions that use the old function's arguments to use the new function's arguments.

Gabriel: look at Function::getBasicBlockList() and
iplist<>::splice(iterator, iplist). Something like
   Function *NewF = Function::Create(NewFnType, OldF->getLinkage());
   NewF->getBasicBlockList().splice(NewF->begin(), OldF->getBasicBlockList());
   NewF->takeName(OldF);
   OldF->eraseFromParent();
is probably what you're looking for.
(Note: completely untested)

-- John T.

Well, of course you may want to also make sure no uses of the old
function remain before erasing it. Since we're talking about main(),
*probably* the only uses you have to worry about are blockaddresses.
You probably should still check for other uses though, just in case
the program is doing something weird (and not supported by the C & C++
standards IIRC).

I don't think a full clone is necessary, since he wants to replace the
function. He only needs to create the new function and splice in the
body of the old one.

That is exactly what MakeFunctionClone() does. It creates a new function
and uses CloneFunctionInto() to create a copy of the instructions in the old
function in the new function. The old function can be removed afterward if
desired.

I'm pretty sure MakeFunctionClone() actually *copies* basic blocks +
instructions instead of moving them over.
Splicing the basic block list into the new function should be much
more efficient, especially for large functions; it's probably
constant-time operation and shouldn't allocate any memory.

I'm not sure if the code below would work. I don't see a mechanism that
updates instructions that use the old function's arguments to use the new
function's arguments.

He's adding explicit argc and argv arguments to main(), implying the
old function didn't have any arguments. But for a general function
it'd be easy enough to call replaceAllUsesWith() on each old argument,
since there's no need to worry about preserving the integrity of the
old function.

Gabriel: look at Function::getBasicBlockList() and
iplist<>::splice(iterator, iplist). Something like
Function *NewF = Function::Create(NewFnType, OldF->getLinkage());
NewF->getBasicBlockList().splice(NewF->begin(),
OldF->getBasicBlockList());
NewF->takeName(OldF);
OldF->eraseFromParent();
is probably what you're looking for.
(Note: completely untested)

And I did put a disclaimer about correctness :).

Reading MakeFunctionClone() though, I do see some other things that I missed:
* A Module* argument to Function::Create (necessitating an empty ""
name argument) so it gets inserted in the module.
* Copying function attributes.
* Copying the calling convention.

I don't think a full clone is necessary, since he wants to replace the
function. He only needs to create the new function and splice in the
body of the old one.

That is exactly what MakeFunctionClone() does. It creates a new function
and uses CloneFunctionInto() to create a copy of the instructions in the old
function in the new function. The old function can be removed afterward if
desired.

I'm pretty sure MakeFunctionClone() actually *copies* basic blocks +
instructions instead of moving them over.

Yes, you're correct. I didn't think that detail was all that important.

Splicing the basic block list into the new function should be much
more efficient, especially for large functions; it's probably
constant-time operation and shouldn't allocate any memory.

Yes, it would more likely be faster.

I'm not sure if the code below would work. I don't see a mechanism that
updates instructions that use the old function's arguments to use the new
function's arguments.

He's adding explicit argc and argv arguments to main(), implying the
old function didn't have any arguments. But for a general function
it'd be easy enough to call replaceAllUsesWith() on each old argument,
since there's no need to worry about preserving the integrity of the
old function.

Gabriel: look at Function::getBasicBlockList() and
iplist<>::splice(iterator, iplist). Something like
   Function *NewF = Function::Create(NewFnType, OldF->getLinkage());
   NewF->getBasicBlockList().splice(NewF->begin(),
OldF->getBasicBlockList());
   NewF->takeName(OldF);
   OldF->eraseFromParent();
is probably what you're looking for.
(Note: completely untested)

And I did put a disclaimer about correctness :).

Reading MakeFunctionClone() though, I do see some other things that I missed:
* A Module* argument to Function::Create (necessitating an empty ""
name argument) so it gets inserted in the module.
* Copying function attributes.
* Copying the calling convention.

Yeah, I think for modifying main(), your approach is better.

-- John T.

Thank you John and Frits. I had been experimenting with the creation of a new function
and the moving of the BBs from the old one into the new one, but couldn’t make it work.
The code Frits proposed works quite well, but if the original main defined argsc but not argsv (which
shouldn’t happen but oh well, both clang and gcc let it through with a warning) then the
verification pass fails due to uses of the old argsc parameter being present in the new body.

oldArgsc->replaceAllUsesWith( newArgsc ) solves this problem completely.

Thank you again,
Gabriel