From 62c8e2b46947a9778b04cb8bcae8a4cc02eb43a9 Mon Sep 17 00:00:00 2001 From: Arina Neshlyaeva Date: Mon, 7 Aug 2023 13:40:02 -0700 Subject: [PATCH] Function template specialization --- docs/ispc.rst | 4 +- src/func.cpp | 31 +++- src/func.h | 4 +- src/module.cpp | 137 +++++++++++------ src/module.h | 16 ++ src/parse.yy | 139 +++++++++++++++--- src/sym.h | 1 + tests/func-template-1.ispc | 31 ++++ .../func_template_not_supported.ispc | 4 +- tests/lit-tests/func_template_spec_1.ispc | 38 +++++ tests/lit-tests/func_template_spec_2.ispc | 33 +++++ tests/lit-tests/func_template_spec_3.ispc | 20 +++ tests/lit-tests/func_template_spec_4.ispc | 32 ++++ tests/lit-tests/func_template_spec_5.ispc | 21 +++ tests/lit-tests/func_template_spec_6.ispc | 38 +++++ 15 files changed, 481 insertions(+), 68 deletions(-) create mode 100644 tests/func-template-1.ispc create mode 100644 tests/lit-tests/func_template_spec_1.ispc create mode 100644 tests/lit-tests/func_template_spec_2.ispc create mode 100644 tests/lit-tests/func_template_spec_3.ispc create mode 100644 tests/lit-tests/func_template_spec_4.ispc create mode 100644 tests/lit-tests/func_template_spec_5.ispc create mode 100644 tests/lit-tests/func_template_spec_6.ispc diff --git a/docs/ispc.rst b/docs/ispc.rst index 9989ab102c6..f990e36dc9e 100644 --- a/docs/ispc.rst +++ b/docs/ispc.rst @@ -3702,12 +3702,14 @@ What is currently supported: function call syntax (i.e. ``add(1, 2);``). * Explicit template function instantiations (i.e. ``template int add(int a, int b);``). +* Explicit template function specializations (i.e. + ``template<> int add(int a, int b) { return a - b;}``). What is currently not supported, but is planned to be supported: * Non-type template parameters. * Default values for template parameters. -* Template function specializations. +* Template arguments deduction in template function specializations. While template argument deduction rules generally follow C++, there are some differences caused by existence of ``uniform``, ``varying`` and ``unbound`` diff --git a/src/func.cpp b/src/func.cpp index 00f4bf10560..26679d890d1 100644 --- a/src/func.cpp +++ b/src/func.cpp @@ -884,7 +884,11 @@ void FunctionTemplate::Print() const { void FunctionTemplate::GenerateIR() const { for (const auto &inst : instantiations) { Function *func = const_cast(inst.second->parentFunction); - func->GenerateIR(); + if (func != nullptr) { + func->GenerateIR(); + } else { + Error(inst.second->pos, "Template function specialization was declared but never defined."); + } } } @@ -984,6 +988,31 @@ Symbol *FunctionTemplate::AddInstantiation(const std::vector> &types, + SourcePos pos) { + const TemplateParms *typenames = GetTemplateParms(); + Assert(typenames); + TemplateInstantiation templInst(*typenames, types); + + // Create a function symbol + Symbol *instSym = templInst.InstantiateTemplateSymbol(sym); + instSym->type = ftype; + instSym->pos = pos; + + TemplateArgs *templArgs = new TemplateArgs(types); + + // Check if we have previously declared specialization and we are about to define it. + Symbol *funcSym = LookupInstantiation(types); + if (funcSym != nullptr) { + delete templArgs; + return funcSym; + } else { + instantiations.push_back(std::make_pair(templArgs, instSym)); + } + return instSym; +} + /////////////////////////////////////////////////////////////////////////// // TemplateInstantiation diff --git a/src/func.h b/src/func.h index 0b771b8550c..3a4d78ee3f9 100644 --- a/src/func.h +++ b/src/func.h @@ -83,8 +83,10 @@ class FunctionTemplate { Symbol *LookupInstantiation(const std::vector> &types); Symbol *AddInstantiation(const std::vector> &types); + Symbol *AddSpecialization(const FunctionType *ftype, const std::vector> &types, + SourcePos pos); - // Generate code for instantiations + // Generate code for instantiations and specializations. void GenerateIR() const; void Print() const; diff --git a/src/module.cpp b/src/module.cpp index 17074c4a03c..f7d10740cd3 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -1136,62 +1136,111 @@ void Module::AddFunctionTemplateDefinition(const TemplateParms *templateParmList ast->AddFunctionTemplate(sym, code); } -void Module::AddFunctionTemplateInstantiation(const std::string &name, - const std::vector> &types, - const FunctionType *ftype, SourcePos pos) { +FunctionTemplate *Module::MatchFunctionTemplate(const std::string &name, const FunctionType *ftype, + std::vector> &normTypes, + SourcePos pos) { + if (ftype == nullptr) { + Assert(m->errorCount > 0); + return nullptr; + } std::vector matches; bool found = symbolTable->LookupFunctionTemplate(name, &matches); + if (!found) { + Error(pos, "No matching function template was found."); + return nullptr; + } + // Do template argument "normalization", i.e apply "varying type default": + // + // template void foo(T t); + // foo(1); // T is assumed to be "varying int" here. + for (auto &arg : normTypes) { + if (arg.first->GetVariability() == Variability::Unbound) { + arg.first = arg.first->GetAsVaryingType(); + } + } - if (found) { - // TODO: need to outline this copy-paste code. - // Do template argument "normalization", i.e apply "varying type default": - // - // template void foo(T t); - // foo(1); // T is assumed to be "varying int" here. - std::vector> normTypes(types); - for (auto &arg : normTypes) { - if (arg.first->GetVariability() == Variability::Unbound) { - arg.first = arg.first->GetAsVaryingType(); - } + FunctionTemplate *templ = nullptr; + for (auto &templateSymbol : matches) { + // Number of template parameters must match. + if (normTypes.size() != templateSymbol->templateParms->GetCount()) { + // We don't have default parameters yet, so just matching the size exactly. + continue; } - FunctionTemplate *templ = nullptr; - for (auto &templateSymbol : matches) { - // Number of template parameters must match. - if (normTypes.size() != templateSymbol->templateParms->GetCount()) { - // We don't have default parameters yet, so just matching the size exactly. - continue; + // Number of function parameters must match. + if (!ftype || !templateSymbol->type || ftype->GetNumParameters() != templateSymbol->type->GetNumParameters()) { + continue; + } + bool matched = true; + TemplateInstantiation inst(*(templateSymbol->templateParms), normTypes); + for (int i = 0; i < ftype->GetNumParameters(); i++) { + const Type *instParam = ftype->GetParameterType(i); + const Type *templateParam = templateSymbol->type->GetParameterType(i)->ResolveDependence(inst); + if (!Type::Equal(instParam, templateParam)) { + matched = false; + break; } + } + if (matched) { + templ = templateSymbol->functionTemplate; + break; + } + } + return templ; +} - // Number of function parameters must match. - if (!ftype || !templateSymbol->type || - ftype->GetNumParameters() != templateSymbol->type->GetNumParameters()) { - continue; - } +void Module::AddFunctionTemplateInstantiation(const std::string &name, + const std::vector> &types, + const FunctionType *ftype, SourcePos pos) { + std::vector> normTypes(types); + FunctionTemplate *templ = MatchFunctionTemplate(name, ftype, normTypes, pos); + if (templ) { + templ->AddInstantiation(normTypes); + } else { + Error(pos, "No matching function template found for instantiation."); + } +} - TemplateInstantiation inst(*(templateSymbol->templateParms), normTypes); - bool matched = true; - for (int i = 0; i < ftype->GetNumParameters(); i++) { - const Type *instParam = ftype->GetParameterType(i); - const Type *templateParam = templateSymbol->type->GetParameterType(i)->ResolveDependence(inst); - if (!Type::Equal(instParam, templateParam)) { - matched = false; - break; - } - } +void Module::AddFunctionTemplateSpecializationDefinition(const std::string &name, const FunctionType *ftype, + const std::vector> &types, + SourcePos pos, Stmt *code) { + std::vector> normTypes(types); + FunctionTemplate *templ = MatchFunctionTemplate(name, ftype, normTypes, pos); + if (templ == nullptr) { + Error(pos, "No matching function template found for specialization."); + return; + } + Symbol *sym = templ->LookupInstantiation(normTypes); + if (sym == nullptr || code == nullptr) { + Assert(m->errorCount > 0); + return; + } + sym->pos = code->pos; - if (matched) { - templ = templateSymbol->functionTemplate; - break; - } - } + // Update already created symbol with real function type and function implementation + sym->type = ftype; + Function *inst = new Function(sym, code); + sym->parentFunction = inst; +} - if (templ) { - templ->AddInstantiation(normTypes); - } else { - Error(pos, "No matching function template found for instantiation."); +void Module::AddFunctionTemplateSpecializationDeclaration(const std::string &name, const FunctionType *ftype, + const std::vector> &types, + SourcePos pos) { + std::vector> normTypes(types); + FunctionTemplate *templ = MatchFunctionTemplate(name, ftype, normTypes, pos); + if (templ == nullptr) { + Error(pos, "No matching function template found for specialization."); + return; + } + Symbol *sym = templ->LookupInstantiation(normTypes); + if (sym != nullptr) { + if (Type::Equal(sym->type, ftype) && sym->parentFunction != nullptr) { + Error(pos, "Template function specialization was already defined."); + return; } } + + templ->AddSpecialization(ftype, normTypes, pos); } void Module::AddExportedTypes(const std::vector> &types) { diff --git a/src/module.h b/src/module.h index 0b44d3e236d..42ebac18a31 100644 --- a/src/module.h +++ b/src/module.h @@ -90,6 +90,14 @@ class Module { const std::vector> &types, const FunctionType *ftype, SourcePos pos); + void AddFunctionTemplateSpecializationDeclaration(const std::string &name, const FunctionType *ftype, + const std::vector> &types, + SourcePos pos); + + void AddFunctionTemplateSpecializationDefinition(const std::string &name, const FunctionType *ftype, + const std::vector> &types, + SourcePos pos, Stmt *code); + /** Adds the given type to the set of types that have their definitions included in automatically generated header files. */ void AddExportedTypes(const std::vector> &types); @@ -98,6 +106,14 @@ class Module { function symbol for it. */ Symbol *AddLLVMIntrinsicDecl(const std::string &name, ExprList *args, SourcePos po); + /** Returns pointer to FunctionTemplate based on template name and template argument types provided. Also makes + template argument types normalization, i.e apply "varying type default": + template void foo(T t); + foo(1); // T is assumed to be "varying int" here. + */ + FunctionTemplate *MatchFunctionTemplate(const std::string &name, const FunctionType *ftype, + std::vector> &normTypes, SourcePos pos); + /** After a source file has been compiled, output can be generated in a number of different formats. */ enum OutputType { diff --git a/src/parse.yy b/src/parse.yy index b489612ec2e..dc3a35c32a7 100644 --- a/src/parse.yy +++ b/src/parse.yy @@ -47,7 +47,7 @@ struct PragmaAttributes { aType = AttributeType::none; unrollType = Globals::pragmaUnrollType::none; count = -1; - } + } AttributeType aType; Globals::pragmaUnrollType unrollType; int count; @@ -95,6 +95,7 @@ static void lSuggestParamListAlternates(); static void lAddDeclaration(DeclSpecs *ds, Declarator *decl); static void lAddTemplateDeclaration(TemplateParms *templateParmList, DeclSpecs *ds, Declarator *decl); +static void lAddTemplateSpecialization(const std::vector> &types, DeclSpecs *ds, Declarator *decl); static void lAddFunctionParams(Declarator *decl); static void lAddMaskToSymbolTable(SourcePos pos); static void lAddThreadIndexCountToSymbolTable(SourcePos pos); @@ -261,7 +262,7 @@ struct ForeachDimension { %type template_identifier %type template_argument_list -%type simple_template_id +%type simple_template_id template_function_specialization_declaration %type template_type_parameter template_int_parameter template_parameter %type template_parameter_list template_head %type template_declaration @@ -383,60 +384,60 @@ launch_expression } | TOKEN_LAUNCH '[' assignment_expression ']' postfix_expression '(' argument_expression_list ')' - { + { ConstExpr *oneExpr = new ConstExpr(AtomicType::UniformInt32, (int32_t)1, @5); Expr *launchCount[3] = {$3, oneExpr, oneExpr}; $$ = new FunctionCallExpr($5, $7, Union(@5,@8), true, launchCount); } | TOKEN_LAUNCH '[' assignment_expression ']' postfix_expression '(' ')' - { + { ConstExpr *oneExpr = new ConstExpr(AtomicType::UniformInt32, (int32_t)1, @5); Expr *launchCount[3] = {$3, oneExpr, oneExpr}; $$ = new FunctionCallExpr($5, new ExprList(Union(@5,@6)), Union(@5,@7), true, launchCount); } | TOKEN_LAUNCH '[' assignment_expression ',' assignment_expression ']' postfix_expression '(' argument_expression_list ')' - { + { ConstExpr *oneExpr = new ConstExpr(AtomicType::UniformInt32, (int32_t)1, @7); Expr *launchCount[3] = {$3, $5, oneExpr}; $$ = new FunctionCallExpr($7, $9, Union(@7,@10), true, launchCount); } | TOKEN_LAUNCH '[' assignment_expression ',' assignment_expression ']' postfix_expression '(' ')' - { + { ConstExpr *oneExpr = new ConstExpr(AtomicType::UniformInt32, (int32_t)1, @7); Expr *launchCount[3] = {$3, $5, oneExpr}; $$ = new FunctionCallExpr($7, new ExprList(Union(@7,@8)), Union(@7,@9), true, launchCount); } | TOKEN_LAUNCH '[' assignment_expression ']' '[' assignment_expression ']' postfix_expression '(' argument_expression_list ')' - { + { ConstExpr *oneExpr = new ConstExpr(AtomicType::UniformInt32, (int32_t)1, @8); Expr *launchCount[3] = {$6, $3, oneExpr}; $$ = new FunctionCallExpr($8, $10, Union(@8,@11), true, launchCount); } | TOKEN_LAUNCH '[' assignment_expression ']' '[' assignment_expression ']' postfix_expression '(' ')' - { + { ConstExpr *oneExpr = new ConstExpr(AtomicType::UniformInt32, (int32_t)1, @8); Expr *launchCount[3] = {$6, $3, oneExpr}; $$ = new FunctionCallExpr($8, new ExprList(Union(@8,@9)), Union(@8,@10), true, launchCount); } | TOKEN_LAUNCH '[' assignment_expression ',' assignment_expression ',' assignment_expression ']' postfix_expression '(' argument_expression_list ')' - { + { Expr *launchCount[3] = {$3, $5, $7}; $$ = new FunctionCallExpr($9, $11, Union(@9,@12), true, launchCount); } | TOKEN_LAUNCH '[' assignment_expression ',' assignment_expression ',' assignment_expression ']' postfix_expression '(' ')' - { + { Expr *launchCount[3] = {$3, $5, $7}; $$ = new FunctionCallExpr($9, new ExprList(Union(@9,@10)), Union(@9,@11), true, launchCount); } | TOKEN_LAUNCH '[' assignment_expression ']' '[' assignment_expression ']' '[' assignment_expression ']' postfix_expression '(' argument_expression_list ')' - { + { Expr *launchCount[3] = {$9, $6, $3}; $$ = new FunctionCallExpr($11, $13, Union(@11,@14), true, launchCount); } | TOKEN_LAUNCH '[' assignment_expression ']' '[' assignment_expression ']' '[' assignment_expression ']' postfix_expression '(' ')' - { + { Expr *launchCount[3] = {$9, $6, $3}; $$ = new FunctionCallExpr($11, new ExprList(Union(@11,@12)), Union(@11,@13), true, launchCount); } @@ -2565,14 +2566,77 @@ template_function_instantiation // Template specialization, a-la // template <> int foo(int) { ... } +template_function_specialization_declaration + : TOKEN_TEMPLATE '<' '>' declaration_specifiers simple_template_id '(' parameter_type_list ')' + { + // Function declarator + Declarator *d = new Declarator(DK_FUNCTION, Union(@1, @8)); + d->child = $5->first; + + if ($7 != nullptr) { + d->functionParams = *$7; + // parameter_type_list returns vector of Declarations that is not needed anymore. + delete $7; + } + std::vector> *templArgs = new std::vector>(*$5->second); + Assert(templArgs); + lAddTemplateSpecialization(*templArgs, $4, d); + m->symbolTable->PushScope(); + + lAddFunctionParams(d); + lAddMaskToSymbolTable(@5); + // deallocate SimpleTemplateIDType returned by simple_template_id + lFreeSimpleTemplateID($5); + $$ = new std::pair(d, templArgs); + } + | TOKEN_TEMPLATE '<' '>' declaration_specifiers simple_template_id '(' ')' + { + Declarator *d = new Declarator(DK_FUNCTION, Union(@1, @5)); + d->child = $5->first; + std::vector> *templArgs = new std::vector>(*$5->second); + Assert(templArgs); + lAddTemplateSpecialization(*templArgs, $4, d); + m->symbolTable->PushScope(); + + lAddMaskToSymbolTable(@5); + // deallocate SimpleTemplateIDType returned by simple_template_id + lFreeSimpleTemplateID($5); + $$ = new std::pair(d, templArgs); + } + | TOKEN_TEMPLATE '<' '>' declaration_specifiers simple_template_id '(' error ')' + { + m->symbolTable->PushScope(); + // deallocate SimpleTemplateIDType returned by simple_template_id + lFreeSimpleTemplateID($5); + $$ = nullptr; + } + ; + template_function_specialization - : TOKEN_TEMPLATE '<' '>' declaration_specifiers declarator ';' + : template_function_specialization_declaration ';' { - Error(@$, "Template function specialization not yet supported."); + if ($1 != nullptr) { + // deallocate TemplateSymbol created in template_declaration + lFreeSimpleTemplateID($1); + } + // End templates parameters definition scope + m->symbolTable->PopScope(); } - | TOKEN_TEMPLATE '<' '>' declaration_specifiers declarator compound_statement + | template_function_specialization_declaration compound_statement { - Error(@$, "Template function specialization not yet supported."); + if ($1 != nullptr) { + Declarator *d = $1->first; + const FunctionType *ftype = CastType(d->type); + if (ftype == nullptr) + AssertPos(@1, m->errorCount > 0); + else { + Stmt *code = $2; + if (code == nullptr) code = new StmtList(@2); + m->AddFunctionTemplateSpecializationDefinition(d->name, ftype, *$1->second, Union(@1, @2), code); + } + lFreeSimpleTemplateID($1); + } + m->symbolTable->PopScope(); } ; @@ -2751,6 +2815,43 @@ lAddTemplateDeclaration(TemplateParms *templateParmList, DeclSpecs *ds, Declarat } } +static void +lAddTemplateSpecialization(const std::vector> &types, DeclSpecs *ds, Declarator *decl) { + if (ds == nullptr || decl == nullptr) + // Error happened earlier during parsing + return; + + decl->InitFromDeclSpecs(ds); + if (ds->typeQualifiers & TYPEQUAL_TASK) { + Error(decl->pos, "'task' not supported for template specializations."); + return; + } + if (ds->typeQualifiers & TYPEQUAL_EXPORT) { + Error(decl->pos, "'export' not supported for template specializations."); + return; + } + if (ds->storageClass == SC_TYPEDEF) { + Error(decl->pos, "Illegal \"typedef\" provided with function template specialization."); + return; + } else { + if (decl->type == nullptr) { + Assert(m->errorCount > 0); + return; + } + + if (types.size() == 0) { + Error(decl->pos, "Template arguments deduction is not yet supported in template function specialization."); + return; + } + const FunctionType *ftype = CastType(decl->type); + if (ftype != nullptr) { + m->AddFunctionTemplateSpecializationDeclaration(decl->name, ftype, types, decl->pos); + } + else { + Error(decl->pos, "Only function template specializations are supported."); + } + } +} /** We're about to start parsing the body of a function; add all of the parameters to the symbol table so that they're available. @@ -2839,10 +2940,10 @@ static void lAddThreadIndexCountToSymbolTable(SourcePos pos) { Symbol *taskIndexSym = new Symbol("taskIndex", pos, type); m->symbolTable->AddVariable(taskIndexSym); - + Symbol *taskCountSym = new Symbol("taskCount", pos, type); m->symbolTable->AddVariable(taskCountSym); - + Symbol *taskIndexSym0 = new Symbol("taskIndex0", pos, type); m->symbolTable->AddVariable(taskIndexSym0); Symbol *taskIndexSym1 = new Symbol("taskIndex1", pos, type); @@ -2850,7 +2951,7 @@ static void lAddThreadIndexCountToSymbolTable(SourcePos pos) { Symbol *taskIndexSym2 = new Symbol("taskIndex2", pos, type); m->symbolTable->AddVariable(taskIndexSym2); - + Symbol *taskCountSym0 = new Symbol("taskCount0", pos, type); m->symbolTable->AddVariable(taskCountSym0); Symbol *taskCountSym1 = new Symbol("taskCount1", pos, type); diff --git a/src/sym.h b/src/sym.h index 4e475960e46..1b068ae0a15 100644 --- a/src/sym.h +++ b/src/sym.h @@ -105,6 +105,7 @@ class TemplateSymbol { // The reason to keep them here for now is that for regular functions it's not stored anywhere in AST, // but attached as attrubutes to llvm::Function when it's created. For templates we need to store this // information in here and use later when the template is instantiated. + // These attributes will be inherited by template functions specializations. bool isInline; bool isNoInline; }; diff --git a/tests/func-template-1.ispc b/tests/func-template-1.ispc new file mode 100644 index 00000000000..a70ce8aa4f4 --- /dev/null +++ b/tests/func-template-1.ispc @@ -0,0 +1,31 @@ +#include "../test_static.isph" + +template noinline uniform float noinline goo(T argGooOne, C argGooTwo) { return 0; } + +template <> noinline uniform float noinline goo(int argGooOne, int argGooTwo) { return 1; } + +template <> noinline uniform float noinline goo(uniform float argGooOne, int argGooTwo) { + return argGooOne; +} + +template uniform float noinline goo(C argGooOne) { return 2; } + +template <> uniform float noinline goo(float argGooOne) { return 3; } + +task void f_fu(uniform float RET[], uniform float aFOO[], uniform float b_val) { + int argFoo0 = aFOO[programIndex]; + int argFoo1 = aFOO[programIndex] / 2; + RET[programIndex] = goo(argFoo0, b_val); // primary template, return 0 + RET[0] = goo((float)argFoo0); // specialized template, return 3 + RET[1] = goo(argFoo0, argFoo1); // primary template with two args, return 1 + RET[2] = goo(argFoo0, argFoo1); // specialized template, return 1 + RET[3] = goo(b_val, argFoo1); // specialized template, return b_val +} + +task void result(uniform float RET[]) { + RET[programIndex] = 0; + RET[0] = 3; + RET[1] = 1; + RET[2] = 1; + RET[3] = 5; +} diff --git a/tests/lit-tests/func_template_not_supported.ispc b/tests/lit-tests/func_template_not_supported.ispc index 8d3fabfcdbd..e579a87b22c 100644 --- a/tests/lit-tests/func_template_not_supported.ispc +++ b/tests/lit-tests/func_template_not_supported.ispc @@ -9,8 +9,8 @@ template T foo(T t) { return tt; } -// CHECK: Template function specialization not yet supported. +// CHECK: Template arguments deduction is not yet supported in template function specialization. template <> int foo(int t); -// CHECK: Template function specialization not yet supported. +// CHECK: Template arguments deduction is not yet supported in template function specialization. template <> int foo(int t) { return 1; } diff --git a/tests/lit-tests/func_template_spec_1.ispc b/tests/lit-tests/func_template_spec_1.ispc new file mode 100644 index 00000000000..76bd9db96ca --- /dev/null +++ b/tests/lit-tests/func_template_spec_1.ispc @@ -0,0 +1,38 @@ +// Check basic scenario for functions specializations. +// RUN: %{ispc} %s --emit-llvm-text --target=host --nostdlib -o - | FileCheck %s + +// Regular function +// CHECK-LABEL: define <{{[0-9]*}} x i32> @goo___vyivyf(<{{[0-9]*}} x i32> %argGooOne, <{{[0-9]*}} x float> %argGooTwo, <{{[0-9]*}} x {{.*}}> %__mask) + +// Specialized function +// CHECK-LABEL: define {{.*}} <{{[0-9]*}} x i32> @goo___vyivyf___vyivyf(<{{[0-9]*}} x i32> %argGooOne, <{{[0-9]*}} x float> %argGooTwo) +// Check that implementation of specialized function is generated from specialization +// CHECK: fmul + +// CHECK-LABEL: define <{{[0-9]*}} x float> @foo +// CHECK: call {{.*}} <{{[0-9]*}} x i32> @goo___vyivyf___vyivyf(<{{[0-9]*}} x i32> %argFoo0, <{{[0-9]*}} x float> %argFoo1) +// CHECK: call {{.*}} <{{[0-9]*}} x i32> @goo___vyivyi___vyivyi(<{{[0-9]*}} x i32> %argFoo0, <{{[0-9]*}} x i32> %argFoo0) + +// Instantiated function +// CHECK-LABEL: define {{.*}} <{{[0-9]*}} x i32> @goo___vyivyi___vyivyi(<{{[0-9]*}} x i32> %argGooOne, <{{[0-9]*}} x i32> %argGooTwo) +// Check that implementation of instantiated function is generated from primary template +// CHECK: add + +template noinline int goo(T argGooOne, C argGooTwo) { + return argGooOne + argGooTwo; +} + +noinline int goo(int argGooOne, float argGooTwo) { + return argGooOne * argGooTwo; +} + +template <> noinline int goo(int argGooOne, float argGooTwo) { + return argGooOne * argGooTwo; +} + +float foo(int argFoo0, float argFoo1) { + float a = goo(argFoo0, argFoo1); + int b = goo(argFoo0, argFoo0); + return a + b; +} + diff --git a/tests/lit-tests/func_template_spec_2.ispc b/tests/lit-tests/func_template_spec_2.ispc new file mode 100644 index 00000000000..8c6a04e2f38 --- /dev/null +++ b/tests/lit-tests/func_template_spec_2.ispc @@ -0,0 +1,33 @@ +// Check that function specialization can be declared, then used, then defined. +// RUN: %{ispc} %s --emit-llvm-text --target=host --nostdlib -o - | FileCheck %s + +// Specialized function +// CHECK-LABEL: define {{.*}} <{{[0-9]*}} x i32> @goo___vyivyf___vyivyf(<{{[0-9]*}} x i32> %argGooOne, <{{[0-9]*}} x float> %argGooTwo) +// Check that implementation of specialized function is generated from specialization +// CHECK: fmul + +// CHECK-LABEL: define <{{[0-9]*}} x float> @foo +// CHECK: call {{.*}} <{{[0-9]*}} x i32> @goo___vyivyf___vyivyf(<{{[0-9]*}} x i32> %argFoo0, <{{[0-9]*}} x float> %argFoo1) +// CHECK: call {{.*}} <{{[0-9]*}} x i32> @goo___vyivyi___vyivyi(<{{[0-9]*}} x i32> %argFoo0, <{{[0-9]*}} x i32> %argFoo0) + +// Instantiated function +// CHECK-LABEL: define {{.*}} <{{[0-9]*}} x i32> @goo___vyivyi___vyivyi(<{{[0-9]*}} x i32> %argGooOne, <{{[0-9]*}} x i32> %argGooTwo) +// Check that implementation of instantiated function is generated from primary template +// CHECK: add + +template noinline int goo(T argGooOne, C argGooTwo) { + return argGooOne + argGooTwo; +} + +template <> noinline int goo(int argGooOne, float argGooTwo); + +float foo(int argFoo0, float argFoo1) { + float a = goo(argFoo0, argFoo1); + int b = goo(argFoo0, argFoo0); + return a + b; +} + +template <> noinline int goo(int argGooOne, float argGooTwo) { + return argGooOne * argGooTwo; +} + diff --git a/tests/lit-tests/func_template_spec_3.ispc b/tests/lit-tests/func_template_spec_3.ispc new file mode 100644 index 00000000000..bd403bccff9 --- /dev/null +++ b/tests/lit-tests/func_template_spec_3.ispc @@ -0,0 +1,20 @@ +// Check that error is produced when template function specialization is defined after it was already created through instantiation. +// RUN: not %{ispc} %s --target=host --nostdlib --nowrap 2>&1 | FileCheck %s + +// CHECK-NOT: Please file a bug report + +// CHECK: Template function specialization was already defined. + +template noinline int goo(T argGooOne, C argGooTwo) { + return argGooOne + argGooTwo; +} + +float foo(int argFoo0, float argFoo1) { + float a = goo(argFoo0, argFoo1); + int b = goo(argFoo0, argFoo0); + return a + b; +} + +template <> noinline int goo(int argGooOne, float argGooTwo) { + return argGooOne * argGooTwo; +} diff --git a/tests/lit-tests/func_template_spec_4.ispc b/tests/lit-tests/func_template_spec_4.ispc new file mode 100644 index 00000000000..00a32d0dd19 --- /dev/null +++ b/tests/lit-tests/func_template_spec_4.ispc @@ -0,0 +1,32 @@ +// Check that error is produced when template function specialization is defined several times. +// RUN: not %{ispc} %s --target=host --nostdlib --nowrap 2>&1 | FileCheck %s + +// CHECK-NOT: Please file a bug report + +template noinline int goo(T argGooOne, T argGooTwo) { + return argGooOne + argGooTwo; +} + +template <> noinline int goo(int argGooOne, int argGooTwo) { + return argGooOne * argGooTwo; +} + +// CHECK: Template function specialization was already defined. +template <> noinline int goo(int argGooOne, int argGooTwo) { + return argGooOne * argGooTwo; +} + +// CHECK: No matching function template found for specialization. +template <> noinline int goo(float argGooOne, float argGooTwo) { + return argGooOne + argGooTwo; +} + +// CHECK: Template function specialization was declared but never defined. +template <> noinline int goo(double argGooOne, double argGooTwo); + +float foo(int argFoo0, float argFoo1) { + float a = goo(argFoo0, (int)argFoo1); + int b = goo((float)argFoo0, argFoo1); + double c = goo((double)argFoo0, (double)argFoo1); + return a + b; +} diff --git a/tests/lit-tests/func_template_spec_5.ispc b/tests/lit-tests/func_template_spec_5.ispc new file mode 100644 index 00000000000..8878283a68b --- /dev/null +++ b/tests/lit-tests/func_template_spec_5.ispc @@ -0,0 +1,21 @@ +// Check that error is produced when template function specialization is defined before primary template. +// RUN: not %{ispc} %s --target=host --nostdlib --nowrap 2>&1 | FileCheck %s + +// CHECK-NOT: Please file a bug report + +// TODO: better diagnostics is needed here +// CHECK: syntax error, unexpected identifier, expecting TOKEN_TEMPLATE_NAME + +template <> noinline int goo(int argGooOne, float argGooTwo) { + return argGooOne * argGooTwo; +} + +template noinline int goo(T argGooOne, C argGooTwo) { + return argGooOne + argGooTwo; +} + +float foo(int argFoo0, float argFoo1) { + float a = goo(argFoo0, argFoo1); + int b = goo(argFoo0, argFoo0); + return a + b; +} diff --git a/tests/lit-tests/func_template_spec_6.ispc b/tests/lit-tests/func_template_spec_6.ispc new file mode 100644 index 00000000000..e82fcf77385 --- /dev/null +++ b/tests/lit-tests/func_template_spec_6.ispc @@ -0,0 +1,38 @@ +// Test function template specialization with nested templates. + +// RUN: %{ispc} %s --emit-llvm-text --target=host -O0 -o - | FileCheck %s + +float k; +template noinline T add(T A, T B) { return A + B; } +template noinline T mul(T A, T B) { return A * B; } +template noinline T fma(T A, T B, T C) { return add(mul(A, B), C); } +template <> noinline float fma(float A, float B, float C) { + return add(mul(A, B), C * k); +} + +// CHECK: define {{.*}} @fma___vyf___vyfvyfvyf +// CHECK: call {{.*}} @mul___vyf___vyfvyf(<{{[0-9]*}} x float> +// CHECK: fmul +// CHECK: call {{.*}} @add___vyf___vyfvyf(<{{[0-9]*}} x float> + +// CHECK: define {{.*}} @mul___vyf___vyfvyf + +// CHECK: define {{.*}} @add___vyf___vyfvyf + +// CHECK: define <{{[0-9]*}} x float> @foo___vyfvyfvyf +// CHECK: call {{.*}} @fma___vyf___vyfvyfvyf(<{{[0-9]*}} x float> + +// CHECK: define <{{[0-9]*}} x i32> @foo___vyivyivyi +// CHECK: call {{.*}} @fma___vyi___vyivyivyi(<{{[0-9]*}} x i32> + +// CHECK: define {{.*}} @fma___vyi___vyivyivyi +// CHECK: call {{.*}} @mul___vyi___vyivyi(<{{[0-9]*}} x i32> +// CHECK-NOT: fmul +// CHECK: call {{.*}} @add___vyi___vyivyi(<{{[0-9]*}} x i32> + +// CHECK: define {{.*}} @mul___vyi___vyivyi + +// CHECK: define {{.*}} @add___vyi___vyivyi + +float foo(float A, float B, float C) { return fma(A, B, C); } +int foo(int A, int B, int C) { return fma(A, B, C); }