diff --git a/proto/autotest/lang/mainsignaturetest.tx b/proto/autotest/lang/mainsignaturetest.tx new file mode 100644 index 00000000..62b7badb --- /dev/null +++ b/proto/autotest/lang/mainsignaturetest.tx @@ -0,0 +1,49 @@ +## tests checking of correct main() signature + +## wrong arguments: + +#experr 1: main( errArgC : Int, args : &[]&[]UByte ) { } + +#experr 1: main( args : &[]&[]UByte, errArg : &[]&[]UByte ) { } + +#experr 1: main( args : &[]&[]Byte ) { } + +#experr 1: main( args : &[]&[]UInt ) { } + +#experr 1: main( args : &[]&[]Bool ) { } + +#experr 1: main( args : &[]&UByte ) { } + +#experr 1: main( args : &[]UByte ) { } + +#experr 1: main( args : &&UByte ) { } + +#experr 1: main( args : &UByte ) { } + +#experr 1: main( args : UByte ) { } + + +## wrong return type: + +#experr 1: main( args : &[]&[]UByte ) -> Bool { return FALSE; } + +#experr 1: main( args : &[]&[]UByte ) -> Float { return 0.0; } + +#experr 1: main( args : &[]&[]UByte ) -> &Int { return new Int(); } + +#experr 1: main( args : &[]&[]UByte ) -> [1]Int { return [ 0I ]; } + + +## correct signature: + +main( args : &[]&[]UByte ) -> Int { + assert args.L == 0; + return 0; +} + + +/* Note: these are also correct signatures but implicitly tested in the other tests: +main( args : &[]&[]UByte ) +main() -> Int +main() +*/ diff --git a/proto/autotest/lang/test.py b/proto/autotest/lang/test.py index 0fa9631f..6c6ce48a 100644 --- a/proto/autotest/lang/test.py +++ b/proto/autotest/lang/test.py @@ -44,6 +44,7 @@ def run_cmd( cmdline, expected_ret_code=0 ): "helloworld.tx", "errtest.tx", + "mainsignaturetest.tx", "inttest.tx", "floattest.tx", diff --git a/proto/src/ast/ast_entitydecls.cpp b/proto/src/ast/ast_entitydecls.cpp index 6223b6dd..81e06f21 100644 --- a/proto/src/ast/ast_entitydecls.cpp +++ b/proto/src/ast/ast_entitydecls.cpp @@ -80,28 +80,7 @@ void TxFieldDeclNode::declaration_pass() { // Note: If declared virtual, the virtual declaration flag is still set on this declaration } - std::string declName = this->fieldDef->fieldName->str(); - if ( declName == "self" ) { - // handle constructor declaration - if ( storage != TXS_INSTANCEMETHOD ) - CERROR( this, "Illegal declaration name for non-constructor member: " << declName ); - declName = CONSTR_IDENT; - flags = flags | TXD_CONSTRUCTOR; - } - else if ( declName == CONSTR_IDENT ) { // built-in - ASSERT( flags & TXD_BUILTIN, "Built-in flag not set: " << flags << " at " << this << " in " << this->context().scope() ); - if ( flags & TXD_INITIALIZER ) { - ASSERT( storage == TXS_STATIC, - "Initializer not a static field: " << storage << " at " << this << " in " << this->context().scope() ); - } - else { - ASSERT( flags & TXD_CONSTRUCTOR, "Constructor flag not set: " << flags << " at " << this << " in " << this->context().scope() ); - ASSERT( storage == TXS_INSTANCEMETHOD, - "Constructor not an instance method: " << storage << " at " << this << " in " << this->context().scope() ); - } - } - - this->fieldDef->declare_field( declName, lexContext.scope(), flags, storage ); + this->fieldDef->declare_field( lexContext.scope(), flags, storage ); // Note: Field is processed in the 'outer' scope and not in the 'inner' scope of its declaration. } diff --git a/proto/src/ast/ast_entitydecls_codegen.cpp b/proto/src/ast/ast_entitydecls_codegen.cpp index 60ebb217..495fff83 100644 --- a/proto/src/ast/ast_entitydecls_codegen.cpp +++ b/proto/src/ast/ast_entitydecls_codegen.cpp @@ -10,20 +10,16 @@ void TxTypeDeclNode::code_gen( LlvmGenerationContext& context ) const { LOG_DEBUG( this->LOGGER(), "Skipping codegen for AST of type with ExpErr context: " << this->typeCreatingNode->qtype() ); return; } + + // all "vtable types" except the ones that are type-generic-dependent are generated: auto type = this->typeCreatingNode->qtype(); - if ( type->is_type_generic_dependent() ) { - LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of generic-dependent type: " + if ( !type->has_runtime_type_id() || type->get_runtime_type_id() >= this->registry().vtable_types_count() ) { + LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of non-vtable type: " << this->typeCreatingNode << " : " << this->typeCreatingNode->qtype() ); - // Note that this skips codegen for the entire AST of all generic-dependent types, - // which means none of their members are generated, including any statically declared inner/local types. - // FUTURE: Evaluate capability for generic types to have global static members (e.g. inner types independent of the outer type parameters). -// if ( !( type->is_builtin() || !type->has_runtime_type_id() -// || type->get_runtime_type_id() >= this->registry().vtable_types_count() ) ) -// LOG_NOTE( this->LOGGER(), "Skipping codegen for AST of generic-dependent type that has vtable type id: " << type ); return; } - if ( !type->has_runtime_type_id() || type->get_runtime_type_id() >= this->registry().vtable_types_count() ) { - LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of non-vtable type: " + else if ( type->suppress_code_gen() ) { + LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of suppressed-code-gen type: " << this->typeCreatingNode << " : " << this->typeCreatingNode->qtype() ); return; } diff --git a/proto/src/ast/ast_fielddef_node.cpp b/proto/src/ast/ast_fielddef_node.cpp index 4921425b..272d3dc2 100644 --- a/proto/src/ast/ast_fielddef_node.cpp +++ b/proto/src/ast/ast_fielddef_node.cpp @@ -4,6 +4,8 @@ #include "ast_declpass.hpp" #include "type/ast_types.hpp" +#include "symbol/package.hpp" + const TxField* TxFieldDefiningNode::resolve_field() { ASSERT( this->is_context_set(), "Declaration pass has not been run (lexctx not set) before resolving " << this ); @@ -141,3 +143,88 @@ void TxFieldDefiningNode::verification_pass() const { // TODO: check that constructor function type has void return value } } + + + +void TxLocalFieldDefNode::declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) { + this->declaration = scope->declare_field( this->fieldName->str(), this, declFlags, storage, TxIdentifier() ); +} + + +void TxNonLocalFieldDefNode::declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) { + std::string declName = this->fieldName->str(); + if ( declName == "self" ) { + // handle constructor declaration + if ( storage != TXS_INSTANCEMETHOD ) + CERROR( this, "Illegal declaration name for non-constructor member: " << declName ); + declName = CONSTR_IDENT; + declFlags = declFlags | TXD_CONSTRUCTOR; + } + else if ( declName == CONSTR_IDENT ) { // built-in + ASSERT( declFlags & TXD_BUILTIN, "Built-in flag not set: " << declFlags << " at " << this << " in " << this->context().scope() ); + if ( declFlags & TXD_INITIALIZER ) { + ASSERT( storage == TXS_STATIC, + "Initializer not a static field: " << storage << " at " << this << " in " << this->context().scope() ); + } + else { + ASSERT( declFlags & TXD_CONSTRUCTOR, "Constructor flag not set: " << declFlags << " at " << this << " in " << this->context().scope() ); + ASSERT( storage == TXS_INSTANCEMETHOD, + "Constructor not an instance method: " << storage << " at " << this << " in " << this->context().scope() ); + } + } + + // Note: Field is processed in the 'outer' scope and not in the 'inner' scope of its declaration. + this->declaration = scope->declare_field( declName, this, declFlags, storage, TxIdentifier() ); +} + +bool TxNonLocalFieldDefNode::is_main_signature_valid( const TxActualType* funcType ) const { + auto retType = funcType->return_type(); + bool retOk = bool( retType->get_type_class() == TXTC_VOID + || retType->is_a( *this->context().package()->registry().get_builtin_type( TXBT_INTEGER ) ) ); + if ( !retOk ) + CERROR( this, "main() method has non-integer return type (must be void or integer): " << retType ); + + auto & argTypes = funcType->argument_types(); + if ( argTypes.size() == 0 ) + return retOk; + else if ( argTypes.size() == 1 ) { + const TxActualType* argsType = argTypes.at( 0 ); + if ( argsType->get_type_class() == TXTC_REFERENCE ) { + auto targetType = argsType->target_type(); + if ( targetType->get_type_class() == TXTC_ARRAY ) { + auto elemType = targetType->element_type(); + if ( elemType->get_type_class() == TXTC_REFERENCE ) { + auto elemTargetType = elemType->target_type(); + if ( elemTargetType->get_type_class() == TXTC_ARRAY ) { + auto elemTargetElemType = elemTargetType->element_type(); + if ( elemTargetElemType->is_builtin( TXBT_UBYTE ) ) + return retOk; + } + } + } + } + CERROR( this, "main() method has invalid argument [required signature is main() or main( &[]&[]UByte )] : " << argsType ); + } + else + CERROR( this, "main() method has too many arguments [required signature is main() or main( &[]&[]UByte )]" ); + return false; +} + +void TxNonLocalFieldDefNode::resolution_pass() { + TxFieldDefiningNode::resolution_pass(); + + // handle main() function declarations: + if ( this->fieldName->str() == "main" ) { + auto funcField = this->field(); + if ( funcField->qtype()->get_type_class() == TXTC_FUNCTION ) { + // verify main program function candidate + if ( !( funcField->get_storage() == TXS_GLOBAL || funcField->get_storage() == TXS_STATIC ) ) + CERROR( this, "main() method must have global or static storage: " << funcField->get_storage() ); + if ( is_main_signature_valid( funcField->qtype().type() ) ) { + // register main program function candidate + this->context().package()->registerMainFunc( this->declaration ); + } + } + // non-function symbols declared with the name 'main' are allowed + } +} diff --git a/proto/src/ast/ast_fielddef_node.hpp b/proto/src/ast/ast_fielddef_node.hpp index 5a7c5ca1..6926f129 100644 --- a/proto/src/ast/ast_fielddef_node.hpp +++ b/proto/src/ast/ast_fielddef_node.hpp @@ -79,14 +79,11 @@ class TxFieldDefiningNode : public TxEntityResolvingNode { /** Performs the declaration of the field defined by this node. To be run before declaration pass is run on this node. */ - inline void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) { - this->declare_field( this->fieldName->str(), scope, declFlags, storage ); - } - - /** Performs the declaration of the field defined by this node. To be run before declaration pass is run on this node. */ - inline void declare_field( const std::string& name, TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) { - this->declaration = scope->declare_field( name, this, declFlags, storage, TxIdentifier() ); - } + virtual void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) = 0; +// /** Performs the declaration of the field defined by this node. To be run before declaration pass is run on this node. */ +// inline void declare_field( const std::string& name, TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) { +// this->declaration = scope->declare_field( name, this, declFlags, storage, TxIdentifier() ); +// } virtual TxExpressionNode* get_init_expression() const { return this->initExpression; @@ -141,6 +138,8 @@ class TxLocalFieldDefNode : public TxFieldDefiningNode { return new TxLocalFieldDefNode( this->ploc, this->fieldName->str(), typeExpr, initExpr, this->modifiable, this->_explicit ); } + virtual void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) override; + virtual llvm::Value* code_gen_field_decl( LlvmGenerationContext& context ) const override; void code_gen_field( LlvmGenerationContext& context, GenScope* scope ) const; @@ -149,11 +148,16 @@ class TxLocalFieldDefNode : public TxFieldDefiningNode { class TxNonLocalFieldDefNode : public TxFieldDefiningNode { void inner_code_gen_field( LlvmGenerationContext& context, bool genBody ) const; + bool is_main_signature_valid( const TxActualType* funcType ) const; + TxNonLocalFieldDefNode( const TxLocation& ploc, const std::string& fieldName, TxTypeExpressionNode* typeExpression, TxExpressionNode* initExpression, bool modifiable ) : TxFieldDefiningNode( ploc, fieldName, typeExpression, initExpression, modifiable, false ) { } +protected: + virtual void resolution_pass() override; + public: TxNonLocalFieldDefNode( const TxLocation& ploc, const std::string& fieldName, TxTypeExpressionNode* typeExpression, TxExpressionNode* initExpression ) @@ -171,6 +175,8 @@ class TxNonLocalFieldDefNode : public TxFieldDefiningNode { return new TxNonLocalFieldDefNode( this->ploc, this->fieldName->str(), typeExpr, initExpr, this->modifiable ); } + virtual void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) override; + /** Generates this field, potentially only as a declaration without initializer. Invoked from code referencing this field. */ virtual llvm::Value* code_gen_field_decl( LlvmGenerationContext& context ) const override; diff --git a/proto/src/ast/ast_node.cpp b/proto/src/ast/ast_node.cpp index 9445f883..c2514f5b 100644 --- a/proto/src/ast/ast_node.cpp +++ b/proto/src/ast/ast_node.cpp @@ -13,10 +13,9 @@ unsigned TxNode::nextNodeId = 0; std::string TxNode::str() const { auto ident = this->get_descriptor(); - const size_t bsize = 128; + const size_t bsize = 512; char buf[bsize]; - std::string filename = ploc.begin.filename ? get_file_name( *ploc.begin.filename ) : ""; - snprintf( buf, bsize, "%s %-11s %4u %-24s %s", filename.c_str(), this->parse_loc_string().c_str(), + snprintf( buf, bsize, "%-13s %4u %-24s %s", this->parse_loc_string().c_str(), this->get_node_id(), typeid(*this).name(), ident.c_str() ); if ( this->lexContext.reinterpretationDefiner ) return std::string( buf ) + " <: " + this->lexContext.reinterpretationDefiner->str(); @@ -25,14 +24,16 @@ std::string TxNode::str() const { } std::string TxNode::parse_loc_string() const { - const size_t bsize = 32; + const size_t bsize = 256; char buf[bsize]; + std::string filename = ploc.begin.filename ? get_file_name( *ploc.begin.filename ) : ""; if ( ploc.begin.line == ploc.end.line ) { int lcol = ( ploc.end.column > ploc.begin.column ) ? ploc.end.column : ploc.end.column; - snprintf( buf, bsize, "%3d.%2d-%d", ploc.begin.line, ploc.begin.column, lcol ); + snprintf( buf, bsize, "%s:%3d.%2d-%d", filename.c_str(), ploc.begin.line, ploc.begin.column, lcol ); } else - snprintf( buf, bsize, "%3d.%2d-%d.%d", ploc.begin.line, ploc.begin.column, ploc.end.line, ploc.end.column ); + snprintf( buf, bsize, "%s:%3d.%2d-%d.%d", filename.c_str(), ploc.begin.line, ploc.begin.column, + ploc.end.line, ploc.end.column ); return std::string( buf ); } diff --git a/proto/src/ast/expr/ast_exprs.cpp b/proto/src/ast/expr/ast_exprs.cpp index 35d9dc3a..9236cd34 100644 --- a/proto/src/ast/expr/ast_exprs.cpp +++ b/proto/src/ast/expr/ast_exprs.cpp @@ -132,7 +132,6 @@ TxQualType TxFunctionCallNode::define_type( TxPassInfo passInfo ) { } } -// auto funcType = static_cast( actualCalleeType ); if ( this->calleeType->modifiable_closure() && !constructorType ) { ASSERT( this->callee->get_data_graph_origin_expr(), "Callee with modifiable closere didn't have origin expression: " << this->callee ); if ( !this->callee->get_data_graph_origin_expr()->check_chain_mutable() ) { diff --git a/proto/src/ast/expr/ast_field.cpp b/proto/src/ast/expr/ast_field.cpp index 4cbe5f25..6e34bb3c 100644 --- a/proto/src/ast/expr/ast_field.cpp +++ b/proto/src/ast/expr/ast_field.cpp @@ -72,7 +72,7 @@ const TxFieldDeclaration* resolve_field( const TxExpressionNode* origin, TxEntit // first screen the fields that are of function type and take the correct number of arguments: if ( field->qtype()->get_type_class() == TXTC_FUNCTION ) { - const TxFunctionType* fieldType = static_cast( field->qtype().type() ); + auto fieldType = field->qtype().type(); auto candArgTypes = fieldType->argument_types(); const TxActualType* arrayArgElemType = fieldType->vararg_elem_type(); const TxActualType* fixedArrayArgType = nullptr; diff --git a/proto/src/ast/expr/ast_lambda_codegen.cpp b/proto/src/ast/expr/ast_lambda_codegen.cpp index 2949c771..35424dc7 100644 --- a/proto/src/ast/expr/ast_lambda_codegen.cpp +++ b/proto/src/ast/expr/ast_lambda_codegen.cpp @@ -48,7 +48,7 @@ Function* TxLambdaExprNode::code_gen_function_decl( LlvmGenerationContext& conte ASSERT( funcT, "Couldn't get LLVM type for function type " << this->funcHeaderNode->qtype() ); Function* function = cast( context.llvmModule().getOrInsertFunction( funcName, funcT ) ); - function->setLinkage(GlobalValue::InternalLinkage); // (Note, could earlier cause LLVM to rename function) + function->setLinkage( GlobalValue::InternalLinkage ); // Note: function is of LLVM function pointer type (since it is an LLVM global value) return function; } diff --git a/proto/src/ast/expr/ast_ref.hpp b/proto/src/ast/expr/ast_ref.hpp index affda586..0368781f 100644 --- a/proto/src/ast/expr/ast_ref.hpp +++ b/proto/src/ast/expr/ast_ref.hpp @@ -20,6 +20,11 @@ llvm::Constant* gen_get_ref_typeid ( LlvmGenerationContext& context, llvm::Const llvm::Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, llvm::Type* refT, llvm::Value* ptrV, llvm::Value* tidV ); llvm::Constant* gen_ref( LlvmGenerationContext& context, llvm::Type* refT, llvm::Constant* ptrC, llvm::Constant* tidC ); +/** Generates a Ref value where the type id equals the specified type's *statically known* type id. + * Caller must ensure this cannot be different from the proper runtime id. + */ +llvm::Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, const TxActualType* refType, llvm::Value* ptrV ); + /** Converts a reference value from one type to another. If targetTypeId is specified, it will replace the original type id. */ llvm::Value* gen_ref_conversion( LlvmGenerationContext& context, GenScope* scope, llvm::Value* origRefV, llvm::Type* targetRefT, uint32_t targetTypeId = UINT32_MAX ); diff --git a/proto/src/ast/expr/ast_ref_codegen.cpp b/proto/src/ast/expr/ast_ref_codegen.cpp index ce61e588..754b5765 100644 --- a/proto/src/ast/expr/ast_ref_codegen.cpp +++ b/proto/src/ast/expr/ast_ref_codegen.cpp @@ -53,6 +53,13 @@ Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, Type* refT, Val return refV; } +Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, const TxActualType* refType, Value* ptrV ) { + ASSERT( refType->get_type_class() == TXTC_REFERENCE, "Not a reference type: " << refType ); + auto refT = context.get_llvm_type( refType ); + auto tidV = refType->target_type()->gen_typeid( context ); + return gen_ref( context, scope, refT, ptrV, tidV ); +} + Constant* gen_get_ref_pointer( LlvmGenerationContext& context, Constant* refC ) { return refC->getAggregateElement( 0U ); diff --git a/proto/src/ast/type/ast_typecreating_node.hpp b/proto/src/ast/type/ast_typecreating_node.hpp index 2d253de9..33ecece2 100644 --- a/proto/src/ast/type/ast_typecreating_node.hpp +++ b/proto/src/ast/type/ast_typecreating_node.hpp @@ -79,7 +79,7 @@ class TxAliasTypeNode : public TxTypeCreatingNode { } - virtual void code_gen_type( LlvmGenerationContext& context ) const override; + virtual void code_gen_type( LlvmGenerationContext& context ) const override { } virtual void visit_descendants( const AstVisitor& visitor, const AstCursor& thisCursor, const std::string& role, void* context ) override { this->baseTypeNode->visit_ast( visitor, thisCursor, "basetype", context ); diff --git a/proto/src/ast/type/ast_types_codegen.cpp b/proto/src/ast/type/ast_types_codegen.cpp index 6ecacba2..a4f229d7 100644 --- a/proto/src/ast/type/ast_types_codegen.cpp +++ b/proto/src/ast/type/ast_types_codegen.cpp @@ -47,10 +47,6 @@ void TxEmptyDerivedTypeNode::code_gen_type( LlvmGenerationContext& context ) con this->baseTypeNode->code_gen_type( context ); } -void TxAliasTypeNode::code_gen_type( LlvmGenerationContext& context ) const { - this->baseTypeNode->code_gen_type( context ); -} - void TxDerivedTypeNode::code_gen_builtin_type( LlvmGenerationContext& context ) const { this->inner_code_gen_type( context ); } diff --git a/proto/src/driver.cpp b/proto/src/driver.cpp index 5c204e2d..a409f37b 100644 --- a/proto/src/driver.cpp +++ b/proto/src/driver.cpp @@ -482,15 +482,10 @@ int TxDriver::llvm_compile( const std::string& outputFileName ) { bool mainGenerated = false; if ( auto funcDecl = this->package->getMainFunc() ) { - auto funcField = funcDecl->get_definer()->resolve_field(); - if ( funcField->qtype()->get_type_class() == TXTC_FUNCTION ) { - auto retType = static_cast(funcField->qtype().type())->return_type(); - if ( retType->get_type_class() != TXTC_VOID - && !retType->is_a( *this->package->registry().get_builtin_type( TXBT_INTEGER ) ) ) - this->_LOG.error( "main() method had invalid return type: %s", retType->str().c_str() ); - else if ( ( mainGenerated = this->genContext->generate_main( funcDecl->get_unique_full_name(), funcField->qtype().type() ) ) ) - this->_LOG.debug( "Created program entry for user method %s", funcDecl->get_unique_full_name().c_str() ); - } + auto funcField = funcDecl->get_definer()->field(); + this->genContext->generate_main( funcDecl->get_unique_full_name(), funcField->qtype().type() ); + mainGenerated = true; + LOG_DEBUG( &_LOG, "Generated program entry for user main method " << funcDecl ); } _LOG.info( "+ LLVM code generated (not yet written)" ); diff --git a/proto/src/driver.hpp b/proto/src/driver.hpp index 9fde6703..56edd5b8 100644 --- a/proto/src/driver.hpp +++ b/proto/src/driver.hpp @@ -35,6 +35,8 @@ class TxOptions { bool allow_tx = false; std::string txPath; std::vector sourceSearchPaths; + int jit_argc = 0; + const char** jit_argv = nullptr; }; /** Represents a Tuplex package compilation job. diff --git a/proto/src/llvm_exec.cpp b/proto/src/llvm_exec.cpp index 358eef0a..521b0ae4 100644 --- a/proto/src/llvm_exec.cpp +++ b/proto/src/llvm_exec.cpp @@ -4,6 +4,8 @@ #include #include "llvm_generator.hpp" +#include "driver.hpp" +#include "symbol/package.hpp" using namespace llvm; @@ -25,8 +27,10 @@ int LlvmGenerationContext::run_code() { return -1; } - std::vector noargs; - GenericValue v = ee->runFunction( this->entryFunction, noargs ); + std::vector stdargs( 2 ); + stdargs[0].IntVal = APInt( 32, this->tuplexPackage.driver().get_options().jit_argc ); + stdargs[1].PointerVal = this->tuplexPackage.driver().get_options().jit_argv; + GenericValue v = ee->runFunction( this->entryFunction, stdargs ); int64_t retVal = v.IntVal.getSExtValue(); if ( kind == EngineKind::Kind::Interpreter ) diff --git a/proto/src/llvm_generator.cpp b/proto/src/llvm_generator.cpp index 78e7415c..eb4e22ba 100644 --- a/proto/src/llvm_generator.cpp +++ b/proto/src/llvm_generator.cpp @@ -73,10 +73,8 @@ int LlvmGenerationContext::generate_code( const TxTypeDeclNode* staticScopeNode } } -bool LlvmGenerationContext::generate_main( const std::string& userMainIdent, const TxActualType* mainFuncType ) { - bool ret = ( static_cast( mainFuncType )->return_type()->get_type_class() != TXTC_VOID ); - this->entryFunction = this->gen_main_function( userMainIdent, ret ); - return this->entryFunction; +void LlvmGenerationContext::generate_main( const std::string& userMainIdent, const TxActualType* mainFuncType ) { + this->entryFunction = this->gen_main_function( userMainIdent, mainFuncType ); } void LlvmGenerationContext::initialize_target() { @@ -176,10 +174,6 @@ static Constant* gen_supertype_ids( LlvmGenerationContext* context, const TxActu add_base_types( supertypes, static_cast(acttype)->adapted_type() ); std::vector supertypesvec( supertypes.cbegin(), supertypes.cend() ); -// if ( acttype->get_type_class() == TXTC_INTERFACEADAPTER ) { -// std::cerr << "Supertypes of " << acttype << " with id " << acttype->get_runtime_type_id() << std::endl; -// for ( auto t : supertypesvec ) std::cerr << " super type id: " << t << std::endl; -// } auto int32T = Type::getInt32Ty( context->llvmContext ); auto typesArrayLenC = ConstantInt::get( int32T, supertypesvec.size() ); @@ -461,14 +455,20 @@ llvm::Value* LlvmGenerationContext::gen_malloc( GenScope* scope, Value* sizeV ) /*** runtime type info access ***/ Value* LlvmGenerationContext::gen_get_type_info( GenScope* scope, const TxActualType* statDeclType, Value* runtimeBaseTypeIdV, unsigned fieldIndex ) { + //std::cerr << "gen_get_type_info() " << statDeclType << std::endl; auto typeInfoC = cast( this->lookup_llvm_value( "tx.runtime.TYPE_INFOS" ) ); if ( auto baseTypeIdC = dyn_cast( runtimeBaseTypeIdV ) ) { - //std::cerr << "Getting static type info A of " << statDeclType << std::endl; + //std::cerr << "Getting static type info of " << statDeclType << " and constant type id " << baseTypeIdC << std::endl; + auto tid = cast( baseTypeIdC )->getZExtValue(); + if ( tid >= this->tuplexPackage.registry().data_types_count() ) + THROW_LOGIC( "(constant) type id out of bounds (not a data type) in gen_get_type_info(): " << tid ); return typeInfoC->getInitializer()->getAggregateElement( baseTypeIdC ) ->getAggregateElement( fieldIndex ); } else if ( statDeclType && statDeclType->is_leaf_derivation() ) { - //std::cerr << "Getting static type info B of " << statDeclType << std::endl; + //std::cerr << "Getting static type info of leaf derivation " << statDeclType << std::endl; + if ( statDeclType->get_runtime_type_id() >= this->tuplexPackage.registry().data_types_count() ) + THROW_LOGIC( "Not a data type in gen_get_type_info(): " << statDeclType ); return typeInfoC->getInitializer()->getAggregateElement( statDeclType->get_runtime_type_id() ) ->getAggregateElement( fieldIndex ); } @@ -778,55 +778,141 @@ Type* LlvmGenerationContext::get_llvm_type( const TxActualType* txType ) { /** Add main function so can be fully compiled * define i32 @main(i32 %argc, i8 **%argv) */ -Function* LlvmGenerationContext::gen_main_function( const std::string userMain, bool hasIntReturnValue ) { +Function* LlvmGenerationContext::gen_main_function( const std::string userMain, const TxActualType* mainFuncType ) { //define i32 @main(i32 %argc, i8 **%argv) - Function *main_func = cast( + Function *mainFunc = cast( this->llvmModule().getOrInsertFunction( "main", this->i32T, this->i32T, PointerType::getUnqual( PointerType::getUnqual( IntegerType::getInt8Ty( this->llvmContext ) ) ), NULL ) ); - { - Function::arg_iterator args = main_func->arg_begin(); - Value *arg_0 = &(*args); - arg_0->setName( "argc" ); - args++; - Value *arg_1 = &(*args); - arg_1->setName( "argv" ); - args++; - } - BasicBlock *bb = BasicBlock::Create( this->llvmContext, "entry", main_func ); + Function::arg_iterator args = mainFunc->arg_begin(); + Value* argcV = &(*args); + argcV->setName( "argc" ); + args++; + Value* argvA = &(*args); + argvA->setName( "argv" ); + + BasicBlock *entryBlock = BasicBlock::Create( this->llvmContext, "entry", mainFunc ); + IRBuilder<> builder( entryBlock ); + GenScope scope( &builder ); // // initialize statics / runtime environment // Function *initFunc = this->gen_static_init_function(); -// CallInst *initCall = CallInst::Create(initFunc, "", bb); +// CallInst *initCall = CallInst::Create(initFunc, "", entryBlock); // initCall->setTailCall(false); //call i32 user main() auto userMainFName = userMain + "$func"; - auto func = this->llvmModule().getFunction( userMainFName ); - if ( func ) { + auto userMainFunc = this->llvmModule().getFunction( userMainFName ); + if ( userMainFunc ) { auto nullClosureRefV = Constant::getNullValue( this->get_closureRefT() ); - Value* args[] = { nullClosureRefV }; - CallInst *user_main_call = CallInst::Create( func, args, "", bb ); - user_main_call->setTailCall( false ); - user_main_call->setIsNoInline(); - if ( hasIntReturnValue ) { + std::vector args = { nullClosureRefV }; + + if ( userMainFunc->getFunctionType()->getNumParams() > 1 ) { + // provide program string arguments + + auto mainArgsRefType = mainFuncType->argument_types().at( 0 ); + auto argRefType = mainArgsRefType->target_type()->element_type().type(); + + auto i8T = Type::getInt8Ty( this->llvmContext ); + auto i64T = Type::getInt64Ty( this->llvmContext ); + auto i8PtrT = i8T->getPointerTo(); + + // declare strlen() function: size_t strlen ( const char * str ); + Function* strlenFunc = Function::Create( FunctionType::get( i32T, { i8PtrT }, false ), + GlobalValue::ExternalLinkage, "strlen", &this->llvmModule() ); + strlenFunc->setCallingConv( CallingConv::C ); + + // declare strcpy() function: char * strcpy ( char * destination, const char * source ); + Function* strcpyFunc = Function::Create( FunctionType::get( i8PtrT, std::vector( { i8PtrT, i8PtrT } ), false ), + GlobalValue::ExternalLinkage, "strcpy", &this->llvmModule() ); + strlenFunc->setCallingConv( CallingConv::C ); + + auto ixA = builder.CreateAlloca( i32T, nullptr, "ix" ); + builder.CreateStore( ConstantInt::get( i32T, 0 ), ixA ); + + auto ubyteArrayT = StructType::get( i32T, i32T, ArrayType::get( i8T, 0 ), NULL ); + auto ubyteArrayRefT = StructType::get( ubyteArrayT->getPointerTo(), i32T, NULL ); + auto argsArrayRefT = cast( userMainFunc->getFunctionType()->getParamType( 1 ) ); + auto argsArrayPtrT = argsArrayRefT->getElementType( 0 ); + + Value* argsArrayA; + { + Value* arrayCap64V = builder.CreateZExtOrBitCast( argcV, i64T ); + Constant* elemSizeC = ConstantExpr::getSizeOf( ubyteArrayRefT ); + Value* objectSizeV = gen_compute_array_size( *this, &scope, elemSizeC, arrayCap64V ); + argsArrayA = builder.CreatePointerCast( builder.Insert( new AllocaInst( i8T, objectSizeV, 8 ) ), argsArrayPtrT, "args" ); + initialize_array_obj( *this, &scope, argsArrayA, argcV, argcV ); + } + + auto argBlock = BasicBlock::Create( this->llvmContext, "arg", mainFunc ); + auto postBlock = BasicBlock::Create( this->llvmContext, "post", mainFunc ); + + { + auto condV = builder.CreateICmpULT( ConstantInt::get( i32T, 0 ), argcV ); + builder.CreateCondBr( condV, argBlock, postBlock ); + } + { + builder.SetInsertPoint( argBlock ); + auto ixV = builder.CreateLoad( ixA ); + + { // get length of cstring arg, allocate tx byte array, copy content, and write ref to tx args array element: + auto argCStrA = builder.CreateLoad( builder.CreateGEP( argvA, { ixV } ) ); + auto argCStrLenV = builder.CreateCall( strlenFunc, { argCStrA } ); + auto argCMemLenV = builder.CreateAdd( argCStrLenV, ConstantInt::get( i32T, 1 ) ); + Value* arrayCap64V = builder.CreateZExtOrBitCast( argCMemLenV, i64T ); + Constant* elemSizeC = ConstantExpr::getSizeOf( i8T ); + Value* objectSizeV = gen_compute_array_size( *this, &scope, elemSizeC, arrayCap64V ); + Value* argArrayObjA = builder.CreatePointerCast( builder.Insert( new AllocaInst( i8T, objectSizeV, 8 ) ), + ubyteArrayT->getPointerTo(), "arg" ); + initialize_array_obj( *this, &scope, argArrayObjA, argCMemLenV, argCStrLenV ); + + auto argByteArrayA = builder.CreateGEP( argArrayObjA, std::vector( { ConstantInt::get( i32T, 0 ), + ConstantInt::get( i32T, 2 ) } ) ); + auto argBytesA = builder.CreatePointerCast( argByteArrayA, i8PtrT ); + builder.CreateCall( strcpyFunc, std::vector( { argBytesA, argCStrA } ) ); + auto argRefV = gen_ref( *this, &scope, argRefType, argArrayObjA ); + auto dstIxs = std::vector( { ConstantInt::get( i32T, 0 ), ConstantInt::get( i32T, 2 ), ixV } ); + builder.CreateStore( argRefV, builder.CreateGEP( argsArrayA, dstIxs ) ); + } + + // increment index and iterate/exit: + auto ixV1 = builder.CreateAdd( ixV, ConstantInt::get( i32T, 1 ) ); + builder.CreateStore( ixV1, ixA ); + auto condV = builder.CreateICmpULT( ixV1, argcV ); + builder.CreateCondBr( condV, argBlock, postBlock ); + } + builder.SetInsertPoint( postBlock ); + + // FIXME: If not in share-value-specializations-code mode, the ref type here must be for an array type with specific length: + auto argsArrayRefV = gen_ref( *this, &scope, mainArgsRefType, argsArrayA ); + args.push_back( argsArrayRefV ); + } + + if ( mainFuncType->has_return_value() ) { + CallInst *userMainCall = builder.CreateCall( userMainFunc, args, "usermain" ); + userMainCall->setTailCall( false ); + userMainCall->setIsNoInline(); + // truncate return value to i32 - CastInst* truncVal = CastInst::CreateIntegerCast( user_main_call, i32T, true, "", bb ); - ReturnInst::Create( this->llvmContext, truncVal, bb ); + auto truncVal = builder.CreateIntCast( userMainCall, i32T, true, "ret" ); + builder.CreateRet( truncVal ); } else { - ReturnInst::Create( this->llvmContext, ConstantInt::get( i32T, 0, true ), bb ); + CallInst *userMainCall = builder.CreateCall( userMainFunc, args ); + userMainCall->setTailCall( false ); + userMainCall->setIsNoInline(); + builder.CreateRet( ConstantInt::get( i32T, 0, true ) ); } } else { this->LOGGER()->error( "LLVM function not found for name: %s", userMain.c_str() ); - ReturnInst::Create( this->llvmContext, ConstantInt::get( this->llvmContext, APInt( 32, 0, true ) ), bb ); + builder.CreateRet( ConstantInt::get( i32T, 0, true ) ); } - return main_func; + return mainFunc; } diff --git a/proto/src/llvm_generator.hpp b/proto/src/llvm_generator.hpp index cc2decd5..d0305896 100644 --- a/proto/src/llvm_generator.hpp +++ b/proto/src/llvm_generator.hpp @@ -81,7 +81,7 @@ class LlvmGenerationContext { llvm::Value* gen_get_type_info( GenScope* scope, const TxActualType* staticType, llvm::Value* runtimeBaseTypeIdV, unsigned fieldIndex ); - llvm::Function* gen_main_function( const std::string userMain, bool hasIntReturnValue ); + llvm::Function* gen_main_function( const std::string userMain, const TxActualType* mainFuncType ); void gen_get_supertypes_array_function(); void gen_array_any_equals_function(); void gen_array_elementary_equals_function(); @@ -176,7 +176,7 @@ class LlvmGenerationContext { /** Create the top level function to call as program entry. * (This is the built-in main, which calls the user main function.) */ - bool generate_main( const std::string& userMainIdent, const TxActualType* mainFuncType ); + void generate_main( const std::string& userMainIdent, const TxActualType* mainFuncType ); void initialize_target(); diff --git a/proto/src/main.cpp b/proto/src/main.cpp index 7b1059d3..ac6b4973 100644 --- a/proto/src/main.cpp +++ b/proto/src/main.cpp @@ -11,7 +11,7 @@ static Logger& LOG = Logger::get( "MAIN" ); -int main( int argc, char **argv ) +int main( int argc, const char **argv ) { // Logger::set_global_threshold(Level::ALL); // for (int lvl=Level::NONE; lvl < Level::ALL; lvl++) @@ -58,6 +58,7 @@ int main( int argc, char **argv ) printf( " %-22s %s\n", "-ver", "Run generated code verifier after successful compilation" ); printf( " %-22s %s\n", "-nojit", "Disable running program in JIT mode after successful compilation (default if release build)" ); printf( " %-22s %s\n", "-jit", "Run program in JIT mode after successful compilation" ); + printf( " %-22s %s\n", "-jo | -jitoptions", "Remaining options are passed to program run in JIT mode" ); printf( " %-22s %s\n", "-nobc", "Don't output bitcode (and if also running in JIT mode, exit with program's return code)" ); printf( " %-22s %s\n", "-bc", "Output bitcode file (default if release build)" ); printf( " %-22s %s\n", "-onlyparse", "Stop after grammar parse" ); @@ -136,6 +137,12 @@ int main( int argc, char **argv ) } outputFileName = argv[a]; } + else if ( !strcmp( argv[a], "-jo" ) || !strcmp( argv[a], "-jitoptions" ) ) { + // remaining options are passed to jit'ed program + options.jit_argc = argc - a - 1; + options.jit_argv = &argv[a+1]; + break; + } else { LOG.error( "No such option '%s' (use -h or -help to print command line usage)", argv[a] ); return 1; // exits diff --git a/proto/src/symbol/package.cpp b/proto/src/symbol/package.cpp index e1164612..5d054609 100644 --- a/proto/src/symbol/package.cpp +++ b/proto/src/symbol/package.cpp @@ -10,22 +10,16 @@ TxPackage::TxPackage( TxDriver& driver, const TxParseOrigin& rootOrigin ) this->builtinTypes = new BuiltinTypes( *this->typeRegistry ); } -void TxPackage::registerMainFunc( const TxEntitySymbol* mainFunc ) { +void TxPackage::registerMainFunc( const TxFieldDeclaration* mainFunc ) { if ( !this->mainFunc ) { this->mainFunc = mainFunc; - this->LOGGER()->debug( "Set user main function: %s", mainFunc->str().c_str() ); + LOG_DEBUG( this->LOGGER(), "Set user main function: " << mainFunc ); } else - this->LOGGER()->debug( "User main function already set, skipping %s", mainFunc->str().c_str() ); + CINFO( mainFunc->get_definer(), "Multiple main() functions, will use first one encountered: " << this->mainFunc->get_unique_full_name() + << " at " << this->mainFunc->get_definer()->parse_loc_string() ); } const TxFieldDeclaration* TxPackage::getMainFunc() const { - if ( this->mainFunc ) { - if ( this->mainFunc->field_count() == 1 ) - return this->mainFunc->get_first_field_decl(); - else if ( this->mainFunc->is_overloaded() ) - CWARNING( this->mainFunc->get_first_field_decl()->get_definer(), - "main() function symbol is overloaded: " << this->mainFunc ); - } - return nullptr; + return this->mainFunc; } diff --git a/proto/src/symbol/package.hpp b/proto/src/symbol/package.hpp index 9832ef78..54119830 100644 --- a/proto/src/symbol/package.hpp +++ b/proto/src/symbol/package.hpp @@ -18,7 +18,7 @@ class TxPackage : public TxModule { TxDriver& _driver; TypeRegistry* typeRegistry; BuiltinTypes* builtinTypes; - const TxEntitySymbol* mainFunc; + const TxFieldDeclaration* mainFunc; public: TxPackage( TxDriver& driver, const TxParseOrigin& rootOrigin ); @@ -40,7 +40,7 @@ class TxPackage : public TxModule { return this->origin; } - void registerMainFunc( const TxEntitySymbol* mainFunc ); + void registerMainFunc( const TxFieldDeclaration* mainFunc ); const TxFieldDeclaration* getMainFunc() const; diff --git a/proto/src/symbol/symbol.cpp b/proto/src/symbol/symbol.cpp index f7692a7c..1e5597ce 100644 --- a/proto/src/symbol/symbol.cpp +++ b/proto/src/symbol/symbol.cpp @@ -100,21 +100,12 @@ TxEntitySymbol* TxScopeSymbol::declare_entity( const std::string& plainName, TxN entitySymbol = dynamic_cast( symbol ); if ( !entitySymbol ) { CERR_THROWDECL( definingNode, "Failed to declare entity symbol, can't overload entities and non-entities under same symbol: " << symbol ); - return nullptr; } } else { entitySymbol = new TxEntitySymbol( this, plainName ); this->declare_symbol( *definingNode, entitySymbol ); this->LOGGER()->trace( " Declared %s", entitySymbol->str().c_str() ); - - // register possible main() function: - if ( plainName == "main" ) { - // TODO: check that public and static function of correct signature: static mod main(args) Int - auto package = dynamic_cast( this->get_root_scope() ); - ASSERT( package, "root scope is not a TxPackage" ); - package->registerMainFunc( entitySymbol ); - } } return entitySymbol; } @@ -128,12 +119,10 @@ const TxTypeDeclaration* TxScopeSymbol::declare_type( const std::string& plainNa ASSERT( !is_internal_name( plainName ) || ( declFlags & ( TXD_IMPLICIT | TXD_CONSTRUCTOR | TXD_INITIALIZER ) ), "Mismatch between name format and IMPLICIT flag for type declaration " << plainName ); - if ( TxEntitySymbol* entitySymbol = this->declare_entity( plainName, typeDefiner ) ) { - auto typeDeclaration = new TxTypeDeclaration( entitySymbol, declFlags, typeDefiner ); - if ( entitySymbol->add_type( typeDeclaration ) ) - return typeDeclaration; - } - return nullptr; + TxEntitySymbol* entitySymbol = this->declare_entity( plainName, typeDefiner ); + auto typeDeclaration = new TxTypeDeclaration( entitySymbol, declFlags, typeDefiner ); + entitySymbol->add_type( typeDeclaration ); + return typeDeclaration; } const TxFieldDeclaration* TxScopeSymbol::declare_field( const std::string& plainName, TxFieldDefiningNode* fieldDefiner, @@ -143,12 +132,10 @@ const TxFieldDeclaration* TxScopeSymbol::declare_field( const std::string& plain ASSERT( !is_internal_name( plainName ) || ( declFlags & ( TXD_IMPLICIT | TXD_CONSTRUCTOR | TXD_INITIALIZER ) ), "Mismatch between name format and IMPLICIT flag for field declaration " << plainName ); - if ( TxEntitySymbol* entitySymbol = this->declare_entity( plainName, fieldDefiner ) ) { - auto fieldDeclaration = new TxFieldDeclaration( entitySymbol, declFlags, fieldDefiner, storage, dataspace ); - if ( entitySymbol->add_field( fieldDeclaration ) ) - return fieldDeclaration; - } - return nullptr; + TxEntitySymbol* entitySymbol = this->declare_entity( plainName, fieldDefiner ); + auto fieldDeclaration = new TxFieldDeclaration( entitySymbol, declFlags, fieldDefiner, storage, dataspace ); + entitySymbol->add_field( fieldDeclaration ); + return fieldDeclaration; } void TxScopeSymbol::dump_symbols() const { @@ -184,33 +171,29 @@ const TxEntityDeclaration* TxEntitySymbol::get_distinct_decl() const { return this->get_first_field_decl(); } -bool TxEntitySymbol::add_type( const TxTypeDeclaration* typeDeclaration ) { +void TxEntitySymbol::add_type( const TxTypeDeclaration* typeDeclaration ) { if ( this->typeDeclaration || !this->fieldDeclarations.empty() ) { CERR_THROWDECL( typeDeclaration->get_definer(), "Can't overload several type declarations under the same name: " << this->get_full_name() ); - return false; } this->typeDeclaration = typeDeclaration; + // declare implicit 'Self' type: if ( !( typeDeclaration->get_decl_flags() & ( TXD_GENPARAM | TXD_GENBINDING ) ) && this->get_name() != "Self" && this->get_name() != "Super" && this->get_name() != "$GenericBase" && !begins_with( this->get_name(), "$Ftype" ) ) { auto definer = typeDeclaration->get_definer(); - if ( TxEntitySymbol* entitySymbol = this->declare_entity( "Self", definer ) ) { - auto selfDeclaration = new TxTypeDeclaration( entitySymbol, TXD_PUBLIC | TXD_IMPLICIT, definer ); - entitySymbol->add_type( selfDeclaration ); - } + TxEntitySymbol* entitySymbol = this->declare_entity( "Self", definer ); + auto selfDeclaration = new TxTypeDeclaration( entitySymbol, TXD_PUBLIC | TXD_IMPLICIT, definer ); + entitySymbol->add_type( selfDeclaration ); } - return true; } -bool TxEntitySymbol::add_field( const TxFieldDeclaration* fieldDeclaration ) { +void TxEntitySymbol::add_field( const TxFieldDeclaration* fieldDeclaration ) { if ( this->typeDeclaration ) { CERR_THROWDECL( fieldDeclaration->get_definer(), "Can't overload both type and field declarations under the same name: " << this->get_full_name() ); - return false; } this->fieldDeclarations.push_back( fieldDeclaration ); - return true; } void TxEntitySymbol::add_type_specialization( const TxTypeDeclaration* typeDeclaration ) { diff --git a/proto/src/symbol/symbol.hpp b/proto/src/symbol/symbol.hpp index 229f5ffb..efec069b 100644 --- a/proto/src/symbol/symbol.hpp +++ b/proto/src/symbol/symbol.hpp @@ -112,7 +112,8 @@ class TxScopeSymbol : public Printable { */ virtual void declare_symbol( const TxParseOrigin& origin, TxScopeSymbol* symbol ); - /** Prepares an entity declaration (adding to an existing or a newly created entity symbol within this scope). */ + /** Prepares an entity declaration (adding to an existing or a newly created entity symbol within this scope). + * @throws exceptions if unsuccessful (never returns null) */ virtual TxEntitySymbol* declare_entity( const std::string& plainName, TxNode* definingNode ); /** Looks up a symbol via this scope. */ @@ -226,9 +227,11 @@ class TxEntitySymbol : public TxScopeSymbol { : TxScopeSymbol( parentScope, name ), typeDeclaration(), fieldDeclarations() { } - bool add_type( const TxTypeDeclaration* typeDeclaration ); + /** @throws exception if unsuccessful */ + void add_type( const TxTypeDeclaration* typeDeclaration ); - bool add_field( const TxFieldDeclaration* fieldDeclaration ); + /** @throws exception if unsuccessful */ + void add_field( const TxFieldDeclaration* fieldDeclaration ); inline bool is_overloaded() const { return this->count() > 1; diff --git a/proto/src/symbol/type_base.hpp b/proto/src/symbol/type_base.hpp index c2ed05a8..e833dcfe 100644 --- a/proto/src/symbol/type_base.hpp +++ b/proto/src/symbol/type_base.hpp @@ -143,6 +143,9 @@ class TxActualType : public TxEntity { //public virtual TxParseOrigin, public Pr /** The runtime type id of this type. */ uint32_t runtimeTypeId = UINT32_MAX; + /** If true, this type and its members is to skip code-generation. */ + bool suppressCodeGen = false; + /** If true, this type is mutable, in which case its instances may be declared modifiable. */ const bool mutableType; @@ -310,6 +313,10 @@ class TxActualType : public TxEntity { //public virtual TxParseOrigin, public Pr * @return true if a data type recursion has been discovered */ bool prepare_members(); + /** @return true if this type and its members is to skip code-generation */ + bool suppress_code_gen() const { + return this->suppressCodeGen; + } inline const TxTypeClassHandler* type_class_handler() const { ASSERT( this->typeClassHandler, "NULL type class handler in " << this ); diff --git a/proto/src/symbol/type_codegen.cpp b/proto/src/symbol/type_codegen.cpp index b01e5817..cfb120b6 100644 --- a/proto/src/symbol/type_codegen.cpp +++ b/proto/src/symbol/type_codegen.cpp @@ -203,7 +203,7 @@ Type* TxArrayTypeClassHandler::make_llvm_externc_type( const TxActualType* type, } -static void initialize_array_obj( LlvmGenerationContext& context, GenScope* scope, Value* arrayObjPtrV, Value* arrayCap ) { +void initialize_array_obj( LlvmGenerationContext& context, GenScope* scope, Value* arrayObjPtrV, Value* arrayCap, Value* arrayLen ) { { // initialize capacity field: Value* ixs[] = { ConstantInt::get( Type::getInt32Ty( context.llvmContext ), 0 ), ConstantInt::get( Type::getInt32Ty( context.llvmContext ), 0 ) }; @@ -215,11 +215,14 @@ static void initialize_array_obj( LlvmGenerationContext& context, GenScope* scop Value* ixs[] = { ConstantInt::get( Type::getInt32Ty( context.llvmContext ), 0 ), ConstantInt::get( Type::getInt32Ty( context.llvmContext ), 1 ) }; auto lenField = scope->builder->CreateInBoundsGEP( arrayObjPtrV, ixs ); - auto zeroVal = ConstantInt::get( Type::getInt32Ty( context.llvmContext ), 0 ); - scope->builder->CreateStore( zeroVal, lenField ); + scope->builder->CreateStore( arrayLen, lenField ); } } +static void initialize_array_obj( LlvmGenerationContext& context, GenScope* scope, Value* arrayObjPtrV, Value* arrayCap ) { + initialize_array_obj( context, scope, arrayObjPtrV, arrayCap, ConstantInt::get( Type::getInt32Ty( context.llvmContext ), 0 ) ); +} + void TxArrayTypeClassHandler::initialize_specialized_obj( const TxActualType* type, LlvmGenerationContext& context, GenScope* scope, Value* objPtrV ) const { auto capExpr = type->capacity(); Value* arrayCapV = capExpr->code_gen_expr( context, scope ); @@ -250,7 +253,7 @@ void TxArrayTypeClassHandler::initialize_specialized_obj( const TxActualType* ty } -static Value* gen_compute_array_size( LlvmGenerationContext& context, GenScope* scope, Value* elemSizeV, Value* arrayCapi64V ) { +Value* gen_compute_array_size( LlvmGenerationContext& context, GenScope* scope, Value* elemSizeV, Value* arrayCapi64V ) { // NOTE: This calculation is only "safe" on outer-most type - if used on an element of an aggregate type, padding effects are missed. Constant* headerSizeC = ConstantExpr::getSizeOf( Type::getInt64Ty( context.llvmContext ) ); elemSizeV = scope->builder->CreateZExtOrBitCast( elemSizeV, Type::getInt64Ty( context.llvmContext ) ); @@ -399,12 +402,11 @@ Type* TxFunctionTypeClassHandler::make_llvm_type( const TxActualType* type, Llvm auto closureRefT = context.get_closureRefT(); std::vector llvmArgTypes; llvmArgTypes.push_back( closureRefT ); // first argument is always the closure object ref - auto ftype = static_cast( type ); - for ( auto argTxType : ftype->argument_types() ) { + for ( auto argTxType : type->argument_types() ) { llvmArgTypes.push_back( context.get_llvm_type( argTxType ) ); LOG_TRACE( context.LOGGER(), "Mapping arg type " << argTxType << " to " << ::to_string(llvmArgTypes.back()) ); } - Type* llvmRetType = ftype->has_return_value() ? context.get_llvm_type( ftype->return_type() ) + Type* llvmRetType = type->has_return_value() ? context.get_llvm_type( type->return_type() ) : llvm::Type::getVoidTy( context.llvmContext ); FunctionType *funcT = FunctionType::get( llvmRetType, llvmArgTypes, false ); @@ -432,13 +434,12 @@ Type* TxExternCFunctionTypeClassHandler::make_llvm_type( const TxActualType* typ Type* TxExternCFunctionTypeClassHandler::make_llvm_externc_type( const TxActualType* type, LlvmGenerationContext& context ) const { // Note: In contrast to regular functions, externc functions don't have a closure object pointer as first argument. std::vector llvmArgTypes; - auto ftype = static_cast( type ); - for ( auto argTxType : ftype->argument_types() ) { + for ( auto argTxType : type->argument_types() ) { llvmArgTypes.push_back( argTxType->make_llvm_externc_type( context ) ); //LOG_INFO( context.LOGGER(), "Mapping C arg type " << argTxType << " to " << ::to_string(llvmArgTypes.back()) ); } - Type* llvmRetType = ftype->has_return_value() ? ftype->return_type()->make_llvm_externc_type( context ) - : llvm::Type::getVoidTy( context.llvmContext ); + Type* llvmRetType = type->has_return_value() ? type->return_type()->make_llvm_externc_type( context ) + : llvm::Type::getVoidTy( context.llvmContext ); FunctionType *funcT = FunctionType::get( llvmRetType, llvmArgTypes, false ); return funcT; } diff --git a/proto/src/symbol/type_composite.hpp b/proto/src/symbol/type_composite.hpp index 42b8bc7c..863c13a9 100644 --- a/proto/src/symbol/type_composite.hpp +++ b/proto/src/symbol/type_composite.hpp @@ -9,6 +9,14 @@ #include "type_base.hpp" +llvm::Value* gen_compute_array_size( LlvmGenerationContext& context, GenScope* scope, + llvm::Value* elemSizeV, llvm::Value* arrayCapi64V ); + +/** Initializes an array object in memory by storing the specified capacity and length values to it. */ +void initialize_array_obj( LlvmGenerationContext& context, GenScope* scope, + llvm::Value* arrayObjPtrV, llvm::Value* arrayCap, llvm::Value* arrayLen ); + + class TxArrayTypeClassHandler final : public TxTypeClassHandler { protected: virtual bool inner_is_assignable_to( const TxActualType* type, const TxActualType* other ) const override; diff --git a/proto/src/symbol/type_registry.cpp b/proto/src/symbol/type_registry.cpp index 05eba716..9f34e44b 100644 --- a/proto/src/symbol/type_registry.cpp +++ b/proto/src/symbol/type_registry.cpp @@ -20,6 +20,8 @@ #include "llvm_generator.hpp" +#define VALUE_SPECS_SHARE_CODE + /** the flags that may be inherited when specializing a type */ static const TxDeclarationFlags DECL_FLAG_FILTER = TXD_VIRTUAL | TXD_PUBLIC | TXD_PROTECTED | TXD_ABSTRACT | TXD_FINAL | TXD_IMPLICIT @@ -107,8 +109,12 @@ void TypeRegistry::prepare_types() { continue; } - if ( type->runtimeTypeId < BuiltinTypeId_COUNT ) // the built-in types are already handled + if ( type->runtimeTypeId < BuiltinTypeId_COUNT ) { + // the built-in types are already added to runtimeTypes + if ( type->runtimeTypeId == TXBT_ARRAY ) + type->suppressCodeGen = true; continue; + } if ( type->get_declaration()->get_definer()->exp_err_ctx() ) { LOG_DEBUG( this->LOGGER(), "Not registering type with ExpErr context as runtime type: " << type); @@ -141,11 +147,6 @@ void TypeRegistry::prepare_types() { continue; } - if ( type->get_type_class() == TXTC_INTERFACE ) { - vtableTypes.push_back( type ); - continue; - } - // Notes: // - Not including full-sized runtime type information about equivalent types is a potential footprint optimization, // but also leads to problems. @@ -153,16 +154,33 @@ void TypeRegistry::prepare_types() { // (though they don't necessitate distinct code generation and vtable). if ( type->is_type_generic_dependent() ) { + // Note that this skips codegen for the entire AST of all type-generic-dependent types, + // which means none of their members are generated, including any statically declared inner/local types. + // FUTURE: Evaluate capability for generic types to have global static members + // (e.g. inner types independent of the outer type parameters). + type->suppressCodeGen = true; vtableTypes.push_back( type ); continue; } +#ifndef VALUE_SPECS_SHARE_CODE else if ( type->is_value_generic() ) { // TODO: This should really be type->is_value_generic_dependent() vtableTypes.push_back( type ); continue; } +#else + else if ( type->is_value_generic() ) { + // Note: There are not "concrete" since size may be unknown, but they can be code-generated + dataTypes.push_back( type ); + continue; + } + else if ( type->is_pure_value_specialization() ) { + // If type is a value specialization, don't code-generate it, but create RTTI if concrete + type->suppressCodeGen = true; + } +#endif - if ( type->is_abstract() ) { + if ( type->is_abstract() ) { // includes interfaces vtableTypes.push_back( type ); continue; } @@ -209,10 +227,17 @@ static void print_type( const TxActualType* type ) { stat = "stat-concr"; else if ( type->is_dynamic() ) stat = "dyn-concr"; + else if ( type->is_value_generic() ) + stat = "value-gen"; else if ( !type->is_same_vtable_type() ) stat = "abstr/vtab"; - printf( "%4d %s %-10s %10s %s\n", type->get_runtime_type_id(), ::to_string( type->get_declaration()->get_decl_flags() ).c_str(), - to_string( type->get_type_class() ).c_str(), stat.c_str(), type->str(false).c_str() ); + printf( "%4d %s %-10s %10s %c %s\n", + type->get_runtime_type_id(), + ::to_string( type->get_declaration()->get_decl_flags() ).c_str(), + to_string( type->get_type_class() ).c_str(), + stat.c_str(), + ( type->suppress_code_gen() ? '-' : 'C' ), + type->str( false ).c_str() ); } void TypeRegistry::dump_types() const { @@ -452,8 +477,7 @@ TxActualType* TypeRegistry::get_inner_type_specialization( const TxTypeResolving std::string newTypeNameStr; const std::vector* bindingsPtr; -//#define FOODEF -#ifdef FOODEF +#ifndef VALUE_SPECS_SHARE_CODE newTypeNameStr = valueSpecTypeName.str(); bindingsPtr = &bindings; #else @@ -548,13 +572,17 @@ TxActualType* TypeRegistry::make_type_specialization( const TxTypeResolvingNode* "baseType definer's parent is not a TxTypeDeclNode: " << baseTypeExpr->parent() ); auto baseDeclNode = static_cast( baseTypeExpr->parent() ); TxTypeCreatingNode* specTypeExpr; +#ifndef VALUE_SPECS_SHARE_CODE + specTypeExpr = baseTypeExpr->make_ast_copy(); +#else if ( typeBindings ) specTypeExpr = baseTypeExpr->make_ast_copy(); else { - // shallow specialization when only VALUE params are bound + // shallow specialization (no AST reinterpretation copy) when only VALUE params are bound auto shallowBaseTypeExpr = new TxTypeDeclWrapperNode( definer->get_parse_location(), baseDecl ); specTypeExpr = new TxDerivedTypeNode( definer->get_parse_location(), shallowBaseTypeExpr, new std::vector() ); } +#endif { // pass on the generic base type to the new specialization via member named $GenericBase: // identify the "source" semantic base type - the nearest one without bindings: diff --git a/proto/src/tx_logging.hpp b/proto/src/tx_logging.hpp index 12d011f7..f703ea34 100644 --- a/proto/src/tx_logging.hpp +++ b/proto/src/tx_logging.hpp @@ -6,7 +6,7 @@ do { \ if ( level <= Logger::globalThreshold ) { \ std::stringstream msg; msg << message; \ - logger->log( level, "%s", msg.str().c_str() ); \ + (logger)->log( level, "%s", msg.str().c_str() ); \ } \ } while (false) @@ -14,7 +14,7 @@ do { \ if ( INFO <= Logger::globalThreshold ) { \ std::stringstream msg; msg << message; \ - logger->info( "%s", msg.str().c_str() ); \ + (logger)->info( "%s", msg.str().c_str() ); \ } \ } while (false) @@ -22,7 +22,7 @@ do { \ if ( NOTE <= Logger::globalThreshold ) { \ std::stringstream msg; msg << message; \ - logger->note( "%s", msg.str().c_str() ); \ + (logger)->note( "%s", msg.str().c_str() ); \ } \ } while (false) @@ -30,7 +30,7 @@ do { \ if ( DEBUG <= Logger::globalThreshold ) { \ std::stringstream msg; msg << message; \ - logger->debug( "%s", msg.str().c_str() ); \ + (logger)->debug( "%s", msg.str().c_str() ); \ } \ } while (false) @@ -39,7 +39,7 @@ do { \ if ( TRACE <= Logger::globalThreshold ) { \ std::stringstream msg; msg << message; \ - logger->trace( "%s", msg.str().c_str() ); \ + (logger)->trace( "%s", msg.str().c_str() ); \ } \ } while (false) #else