This patch adds support for nested subroutines, but not for trampolines (used for taking pointers to nested subroutines; implementing support for trampolines is more tricky since it needs help from the code generators). There are no changes to LLVM itself.
The patch is quite simple because gcc already does the heavy lifting - all that's needed is to emit the static chains when converting to LLVM. The static chain is passed in a register as an additional parameter. For example, consider int nest(int n) { int a; void z(void) { a = 1; } z(); return a; } gcc turns z into void z(struct frame* chain) { chain->a = 1; } and nest into int nest(int n) { struct frame FRAME; int a; z(&FRAME); return FRAME.a; } where struct frame is a record with one integer field called "a". gcc introduces struct frame to hold all the local variables in the parent "nest" that are accessed by the child "z". It rewrites all references to these variables to refer to the corresponding FRAME fields instead, in both the parent and child. It leaves the old variable declarations on the stack (which is why "int a;" is still present), but since they are no longer used, the optimizers will remove them. This patch only touches emission of nested function bodies and calls to nested functions. It emits z as define internal void @z(%struct.frame* inreg %chain) {...} and the call becomes call void @z( %struct.frame* %FRAME inreg ) It doesn't try to teach ConvertType to correctly emit local function types, since gcc doesn't put enough information in the function type to do this, and it's not needed anyway since llvm-convert has enough "fix up dud prototypes" code to handle this situation too. There are a bunch of gcc testcases for nested functions. They all worked as expected [1], except for those involving variable sized objects (which blew up LLVM), and some twisted examples which blew up the gcc nested function code (i.e. long before reaching llvm-convert and this patch). The variable sized object problems don't seem to have anything to do with nested functions. Enjoy! Duncan. [1] For example, the tests using trampolines and/or non-local gotos failed: they are not yet supported.
Index: gcc.llvm.master/gcc/llvm-backend.cpp =================================================================== --- gcc.llvm.master.orig/gcc/llvm-backend.cpp 2007-02-02 09:15:31.000000000 +0100 +++ gcc.llvm.master/gcc/llvm-backend.cpp 2007-02-04 09:39:30.000000000 +0100 @@ -414,9 +414,9 @@ // llvm_emit_code_for_current_function - Top level interface for emitting a // function to the .s file. void llvm_emit_code_for_current_function(tree fndecl) { - if (cfun->static_chain_decl || cfun->nonlocal_goto_save_area) - sorry("%Jnested functions not supported by LLVM", fndecl); - + if (cfun->nonlocal_goto_save_area) + sorry("%Jnon-local gotos not supported by LLVM", fndecl); + if (errorcount || sorrycount) { TREE_ASM_WRITTEN(fndecl) = 1; return; // Do not process broken code. @@ -690,7 +690,7 @@ if (FnEntry == 0) { unsigned CC; const FunctionType *Ty = - TheTypeConverter->ConvertFunctionType(TREE_TYPE(decl), CC); + TheTypeConverter->ConvertFunctionType(TREE_TYPE(decl), NULL, CC); FnEntry = new Function(Ty, Function::ExternalLinkage, Name, TheModule); FnEntry->setCallingConv(CC); Index: gcc.llvm.master/gcc/llvm-convert.cpp =================================================================== --- gcc.llvm.master.orig/gcc/llvm-convert.cpp 2007-02-02 09:19:08.000000000 +0100 +++ gcc.llvm.master/gcc/llvm-convert.cpp 2007-02-04 18:37:57.000000000 +0100 @@ -259,6 +259,7 @@ Name = IDENTIFIER_POINTER(ID); // Determine the FunctionType and calling convention for this function. + tree static_chain = cfun->static_chain_decl; const FunctionType *FTy; unsigned CallingConv; @@ -270,16 +271,19 @@ if (TYPE_ARG_TYPES(TREE_TYPE(FnDecl)) == 0) { FTy = TheTypeConverter->ConvertArgListToFnType(TREE_TYPE(TREE_TYPE(FnDecl)), DECL_ARGUMENTS(FnDecl), + static_chain, CallingConv); #ifdef TARGET_ADJUST_LLVM_CC TARGET_ADJUST_LLVM_CC(CallingConv, TREE_TYPE(FnDecl)); #endif } else { // Otherwise, just get the type from the function itself. - FTy = TheTypeConverter->ConvertFunctionType(TREE_TYPE(FnDecl), CallingConv); + FTy = TheTypeConverter->ConvertFunctionType(TREE_TYPE(FnDecl), + static_chain, + CallingConv); } - // If we've already see this function and created a prototype, and if the + // If we've already seen this function and created a prototype, and if the // proto has the right LLVM type, just use it. if (DECL_LLVM_SET_P(FnDecl) && cast<PointerType>(DECL_LLVM(FnDecl)->getType())->getElementType() == FTy){ @@ -370,8 +374,11 @@ // Handle the DECL_RESULT. ABIConverter.HandleReturnType(TREE_TYPE(TREE_TYPE(FnDecl))); - - for (tree Args = DECL_ARGUMENTS(FnDecl); Args; Args = TREE_CHAIN(Args)) { + + // Prepend the static chain (if any) to the list of arguments. + tree Args = static_chain ? static_chain : DECL_ARGUMENTS(FnDecl); + + while (Args) { const char *Name = "unnamed_arg"; if (DECL_NAME(Args)) Name = IDENTIFIER_POINTER(DECL_NAME(Args)); @@ -399,6 +406,8 @@ ABIConverter.HandleArgument(TREE_TYPE(Args)); Client.clear(); } + + Args = Args == static_chain ? DECL_ARGUMENTS(FnDecl) : TREE_CHAIN(Args); } // If this is not a void-returning function, initialize the RESULT_DECL. @@ -406,9 +415,9 @@ !DECL_LLVM_SET_P(DECL_RESULT(FnDecl))) EmitAutomaticVariableDecl(DECL_RESULT(FnDecl)); - // If this function has nested functions, we should handle the static chain, - // and handle a potential nonlocal_goto_save_area. - if (cfun->static_chain_decl || cfun->nonlocal_goto_save_area) { + // If this function has nested functions, we should handle a potential + // nonlocal_goto_save_area. + if (cfun->nonlocal_goto_save_area) { // Not supported yet. } @@ -1855,9 +1864,23 @@ if (EmitBuiltinCall(exp, fndecl, DestLoc, Res)) return Res; } - + Value *Callee = Emit(TREE_OPERAND(exp, 0), 0); + if (TREE_OPERAND(exp, 2)) { + // This is a direct call to a function using a static chain. We need to + // change the function type to one with an extra parameter for the chain. + assert(fndecl && "Indirect static chain call!"); + tree function_type = TYPE_MAIN_VARIANT(TREE_TYPE(fndecl)); + tree static_chain = TREE_OPERAND(exp, 2); + + unsigned CallingConv; + const Type *Ty = TheTypeConverter->ConvertFunctionType(function_type, + static_chain, + CallingConv); + Callee = CastToType(Instruction::BitCast, Callee, PointerType::get(Ty)); + } + //EmitCall(exp, DestLoc); Value *Result = EmitCallOf(Callee, exp, DestLoc); @@ -2018,7 +2041,11 @@ // Handle the result, including struct returns. ABIConverter.HandleReturnType(TREE_TYPE(exp)); - + + // Pass the static chain, if any, as the first parameter. + if (TREE_OPERAND(exp, 2)) + CallOperands.push_back (Emit(TREE_OPERAND(exp, 2), 0)); + // Loop over the arguments, expanding them and adding them to the op list. const PointerType *PFTy = cast<PointerType>(Callee->getType()); const FunctionType *FTy = cast<FunctionType>(PFTy->getElementType()); Index: gcc.llvm.master/gcc/llvm-internal.h =================================================================== --- gcc.llvm.master.orig/gcc/llvm-internal.h 2007-02-02 10:09:19.000000000 +0100 +++ gcc.llvm.master/gcc/llvm-internal.h 2007-02-04 18:35:29.000000000 +0100 @@ -117,7 +117,8 @@ /// ConvertFunctionType - Convert the specified FUNCTION_TYPE or METHOD_TYPE /// tree to an LLVM type. This does the same thing that ConvertType does, but /// it also returns the function's LLVM calling convention. - const FunctionType *ConvertFunctionType(tree_node *type, + const FunctionType *ConvertFunctionType(tree_node *type, + tree_node *static_chain, unsigned &CallingConv); /// ConvertArgListToFnType - Given a DECL_ARGUMENTS list on an GCC tree, @@ -125,6 +126,7 @@ /// turning "T foo(...)" functions into "T foo(void)" functions. const FunctionType *ConvertArgListToFnType(tree_node *retty, tree_node *arglist, + tree_node *static_chain, unsigned &CallingConv); private: Index: gcc.llvm.master/gcc/llvm-types.cpp =================================================================== --- gcc.llvm.master.orig/gcc/llvm-types.cpp 2007-02-02 10:09:07.000000000 +0100 +++ gcc.llvm.master/gcc/llvm-types.cpp 2007-02-04 20:31:18.000000000 +0100 @@ -416,7 +416,7 @@ return Ty; unsigned CallingConv; - return TypeDB.setType(type, ConvertFunctionType(type, CallingConv)); + return TypeDB.setType(type, ConvertFunctionType(type, NULL, CallingConv)); } case ARRAY_TYPE: { if (const Type *Ty = GET_TYPE_LLVM(type)) @@ -531,7 +531,8 @@ /// fills in Result with the argument types for the function. It returns the /// specified result type for the function. const FunctionType *TypeConverter:: -ConvertArgListToFnType(tree ReturnType, tree Args, unsigned &CallingConv) { +ConvertArgListToFnType(tree ReturnType, tree Args, tree static_chain, + unsigned &CallingConv) { std::vector<const Type*> ArgTys; const Type *RetTy; @@ -539,12 +540,28 @@ TheLLVMABI<FunctionTypeConversion> ABIConverter(Client); ABIConverter.HandleReturnType(ReturnType); + + if (static_chain) + // Pass the static chain as the first parameter. + ABIConverter.HandleArgument(TREE_TYPE(static_chain)); + for (; Args && TREE_TYPE(Args) != void_type_node; Args = TREE_CHAIN(Args)) ABIConverter.HandleArgument(TREE_TYPE(Args)); - return FunctionType::get(RetTy, ArgTys, false); + + FunctionType::ParamAttrsList ParamAttrs; + + if (static_chain) { + // Something for the return type. + ParamAttrs.push_back(FunctionType::NoAttributeSet); + // Pass the static chain in a register. + ParamAttrs.push_back(FunctionType::InRegAttribute); + } + + return FunctionType::get(RetTy, ArgTys, false, ParamAttrs); } -const FunctionType *TypeConverter::ConvertFunctionType(tree type, +const FunctionType *TypeConverter::ConvertFunctionType(tree type, + tree static_chain, unsigned &CallingConv) { const Type *RetTy = 0; std::vector<const Type*> ArgTypes; @@ -558,7 +575,11 @@ #ifdef TARGET_ADJUST_LLVM_CC TARGET_ADJUST_LLVM_CC(CallingConv, type); #endif - + + if (static_chain) + // Pass the static chain as the first parameter. + ABIConverter.HandleArgument(TREE_TYPE(static_chain)); + // Loop over all of the arguments, adding them as we go. tree Args = TYPE_ARG_TYPES(type); for (; Args && TREE_VALUE(Args) != void_type_node; Args = TREE_CHAIN(Args)){ @@ -614,6 +635,10 @@ LLVM_TARGET_INIT_REGPARM(lparam, type); #endif // LLVM_TARGET_ENABLE_REGPARM + if (static_chain) + // Pass the static chain in a register. + ParamAttrs.push_back(FunctionType::InRegAttribute); + for (tree Args = TYPE_ARG_TYPES(type); Args && TREE_VALUE(Args) != void_type_node; Args = TREE_CHAIN(Args)) {
Index: llvm/test/CFrontend/2007-02-05-nested.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ llvm/test/CFrontend/2007-02-05-nested.c 2007-02-05 21:22:43.000000000 +0100 @@ -0,0 +1,48 @@ +// RUN: %llvmgcc -S -fnested-functions -O2 -o - -emit-llvm %s +// PR915 + +extern void o(int); + +void nest(int n) +{ + int a = 0; + int b = 5; + int c = 0; + int d = 7; + + void f (x) + int x; /* K&R style */ + { + int e = 0; + int f = 2; + int g = 0; + + void y (void) + { + c = n; + e = 1; + g = x; + } + + void z (void) + { + a = 4; + g = 3; + } + + a = 5; + y(); + c = x; + z(); + o (e); + o (f); + o (g); + } + + c = 2; + f (6); + o (a); + o (b); + o (c); + o (d); +}
_______________________________________________ llvm-commits mailing list llvm-commits@cs.uiuc.edu http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits