From 5ece6fec04a3e39e55fdc6b133270bccb7b3ee3f Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Thu, 12 Apr 2012 17:28:30 -0700 Subject: [PATCH] Substantial rewrite (again) of decl handling. The decl.* code now no longer interacts with Symbols, but just returns names, types, initializer expressions, etc., as needed. This makes the code a bit more understandable. Fixes issues #171 and #130. --- ast.cpp | 4 +- ast.h | 5 +- decl.cpp | 409 ++++++++++++++-------------------- decl.h | 45 ++-- func.cpp | 30 ++- func.h | 4 +- ispc.h | 9 + module.cpp | 172 +++++++------- module.h | 17 +- parse.yy | 64 +++--- sym.cpp | 6 - sym.h | 7 - tests/func-anon-param.ispc | 15 ++ tests/global-decl-define.ispc | 14 ++ tests/ptr-cast-complex.ispc | 18 ++ 15 files changed, 396 insertions(+), 423 deletions(-) create mode 100644 tests/func-anon-param.ispc create mode 100644 tests/global-decl-define.ispc create mode 100644 tests/ptr-cast-complex.ispc diff --git a/ast.cpp b/ast.cpp index 0c71cdc8..752585f1 100644 --- a/ast.cpp +++ b/ast.cpp @@ -55,10 +55,10 @@ ASTNode::~ASTNode() { // AST void -AST::AddFunction(Symbol *sym, const std::vector &args, Stmt *code) { +AST::AddFunction(Symbol *sym, Stmt *code) { if (sym == NULL) return; - functions.push_back(new Function(sym, args, code)); + functions.push_back(new Function(sym, code)); } diff --git a/ast.h b/ast.h index 0f73677b..f03d7343 100644 --- a/ast.h +++ b/ast.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2011, Intel Corporation + Copyright (c) 2011-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -84,8 +84,7 @@ class AST { public: /** Add the AST for a function described by the given declaration information and source code. */ - void AddFunction(Symbol *sym, const std::vector &args, - Stmt *code); + void AddFunction(Symbol *sym, Stmt *code); /** Generate LLVM IR for all of the functions into the current module. */ diff --git a/decl.cpp b/decl.cpp index 5dab985e..942df0ce 100644 --- a/decl.cpp +++ b/decl.cpp @@ -33,7 +33,7 @@ /** @file decl.cpp @brief Implementations of classes related to turning declarations into - symbols and types. + symbol names and types. */ #include "decl.h" @@ -218,50 +218,44 @@ Declarator::Declarator(DeclaratorKind dk, SourcePos p) : pos(p), kind(dk) { child = NULL; typeQualifiers = 0; + storageClass = SC_NONE; arraySize = -1; - sym = NULL; + type = NULL; initExpr = NULL; } void Declarator::InitFromDeclSpecs(DeclSpecs *ds) { - const Type *t = GetType(ds); - if (t == NULL) { + const Type *baseType = ds->GetBaseType(pos); + InitFromType(baseType, ds); + + if (type == NULL) { Assert(m->errorCount > 0); return; } - Symbol *sym = GetSymbol(); - if (sym != NULL) { - sym->type = t; - sym->storageClass = ds->storageClass; + storageClass = ds->storageClass; + + if (ds->declSpecList.size() > 0 && + dynamic_cast(type) == NULL) { + Error(pos, "__declspec specifiers for non-function type \"%s\" are " + "not used.", type->GetString().c_str()); } } -Symbol * -Declarator::GetSymbol() const { - // The symbol lives at the last child in the chain, so walk down there - // and return the one there. - const Declarator *d = this; - while (d->child != NULL) - d = d->child; - return d->sym; -} - - void Declarator::Print(int indent) const { printf("%*cdeclarator: [", indent, ' '); pos.Print(); lPrintTypeQualifiers(typeQualifiers); - Symbol *sym = GetSymbol(); - if (sym != NULL) - printf("%s", sym->name.c_str()); + printf("%s ", lGetStorageClassName(storageClass)); + if (name.size() > 0) + printf("%s", name.c_str()); else - printf("(null symbol)"); + printf("(unnamed)"); printf(", array size = %d", arraySize); @@ -295,55 +289,8 @@ Declarator::Print(int indent) const { } -Symbol * -Declarator::GetFunctionInfo(DeclSpecs *ds, std::vector *funArgs) { - const FunctionType *type = - dynamic_cast(GetType(ds)); - if (type == NULL) - return NULL; - - Symbol *declSym = GetSymbol(); - Assert(declSym != NULL); - - // Get the symbol for the function from the symbol table. (It should - // already have been added to the symbol table by AddGlobal() by the - // time we get here.) - Symbol *funSym = m->symbolTable->LookupFunction(declSym->name.c_str(), type); - if (funSym == NULL) - // May be NULL due to error earlier in compilation - Assert(m->errorCount > 0); - else - funSym->pos = pos; - - // Walk down to the declarator for the function. (We have to get past - // the stuff that specifies the function's return type before we get to - // the function's declarator.) - Declarator *d = this; - while (d != NULL && d->kind != DK_FUNCTION) - d = d->child; - Assert(d != NULL); - - for (unsigned int i = 0; i < d->functionParams.size(); ++i) { - Symbol *sym = d->GetSymbolForFunctionParameter(i); - if (sym->type == NULL) { - Assert(m->errorCount > 0); - continue; - } - else - sym->type = sym->type->ResolveUnboundVariability(Variability::Varying); - - funArgs->push_back(sym); - } - - if (funSym != NULL) - funSym->type = funSym->type->ResolveUnboundVariability(Variability::Varying); - - return funSym; -} - - -const Type * -Declarator::GetType(const Type *base, DeclSpecs *ds) const { +void +Declarator::InitFromType(const Type *baseType, DeclSpecs *ds) { bool hasUniformQual = ((typeQualifiers & TYPEQUAL_UNIFORM) != 0); bool hasVaryingQual = ((typeQualifiers & TYPEQUAL_VARYING) != 0); bool isTask = ((typeQualifiers & TYPEQUAL_TASK) != 0); @@ -352,12 +299,16 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { if (hasUniformQual && hasVaryingQual) { Error(pos, "Can't provide both \"uniform\" and \"varying\" qualifiers."); - return NULL; + return; } - if (kind != DK_FUNCTION && isTask) + if (kind != DK_FUNCTION && isTask) { Error(pos, "\"task\" qualifier illegal in variable declaration."); - if (kind != DK_FUNCTION && isExported) + return; + } + if (kind != DK_FUNCTION && isExported) { Error(pos, "\"export\" qualifier illegal in variable declaration."); + return; + } Variability variability(Variability::Unbound); if (hasUniformQual) @@ -365,66 +316,76 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { else if (hasVaryingQual) variability = Variability::Varying; - const Type *type = base; - switch (kind) { - case DK_BASE: + if (kind == DK_BASE) { // All of the type qualifiers should be in the DeclSpecs for the // base declarator Assert(typeQualifiers == 0); Assert(child == NULL); - return type; - - case DK_POINTER: + type = baseType; + } + else if (kind == DK_POINTER) { /* For now, any pointer to an SOA type gets the slice property; if we add the capability to declare pointers as slices or not, we'll want to set this based on a type qualifier here. */ - type = new PointerType(type, variability, isConst, type->IsSOAType()); - if (child != NULL) - return child->GetType(type, ds); + const Type *ptrType = new PointerType(baseType, variability, isConst, + baseType->IsSOAType()); + if (child != NULL) { + child->InitFromType(ptrType, ds); + type = child->type; + name = child->name; + } else - return type; - break; - - case DK_REFERENCE: - if (hasUniformQual) + type = ptrType; + } + else if (kind == DK_REFERENCE) { + if (hasUniformQual) { Error(pos, "\"uniform\" qualifier is illegal to apply to references."); - if (hasVaryingQual) + return; + } + if (hasVaryingQual) { Error(pos, "\"varying\" qualifier is illegal to apply to references."); - if (isConst) + return; + } + if (isConst) { Error(pos, "\"const\" qualifier is to illegal apply to references."); - + return; + } // The parser should disallow this already, but double check. - if (dynamic_cast(type) != NULL) { + if (dynamic_cast(baseType) != NULL) { Error(pos, "References to references are illegal."); - return NULL; + return; } - type = new ReferenceType(type); - if (child != NULL) - return child->GetType(type, ds); + const Type *refType = new ReferenceType(baseType); + if (child != NULL) { + child->InitFromType(refType, ds); + type = child->type; + name = child->name; + } else - return type; - break; - - case DK_ARRAY: - if (Type::Equal(type, AtomicType::Void)) { + type = refType; + } + else if (kind == DK_ARRAY) { + if (Type::Equal(baseType, AtomicType::Void)) { Error(pos, "Arrays of \"void\" type are illegal."); - return NULL; + return; } - if (dynamic_cast(type)) { + if (dynamic_cast(baseType)) { Error(pos, "Arrays of references (type \"%s\") are illegal.", - type->GetString().c_str()); - return NULL; + baseType->GetString().c_str()); + return; } - type = new ArrayType(type, arraySize); - if (child) - return child->GetType(type, ds); + const Type *arrayType = new ArrayType(baseType, arraySize); + if (child != NULL) { + child->InitFromType(arrayType, ds); + type = child->type; + name = child->name; + } else - return type; - break; - - case DK_FUNCTION: { + type = arrayType; + } + else if (kind == DK_FUNCTION) { std::vector args; std::vector argNames; std::vector argDefaults; @@ -436,20 +397,40 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { for (unsigned int i = 0; i < functionParams.size(); ++i) { Declaration *d = functionParams[i]; - Symbol *sym = GetSymbolForFunctionParameter(i); - - if (d->declSpecs->storageClass != SC_NONE) - Error(sym->pos, "Storage class \"%s\" is illegal in " - "function parameter declaration for parameter \"%s\".", - lGetStorageClassName(d->declSpecs->storageClass), - sym->name.c_str()); - if (Type::Equal(sym->type, AtomicType::Void)) { - Error(sym->pos, "Parameter with type \"void\" illegal in function " - "parameter list."); - sym->type = NULL; + if (d == NULL) { + Assert(m->errorCount > 0); + continue; + } + if (d->declarators.size() == 0) { + // function declaration like foo(float), w/o a name for the + // parameter; wire up a placeholder Declarator for it + d->declarators.push_back(new Declarator(DK_BASE, pos)); + d->declarators[0]->InitFromDeclSpecs(d->declSpecs); } - const ArrayType *at = dynamic_cast(sym->type); + Assert(d->declarators.size() == 1); + Declarator *decl = d->declarators[0]; + + if (decl->name == "") { + // Give a name to any anonymous parameter declarations + char buf[32]; + sprintf(buf, "__anon_parameter_%d", i); + decl->name = buf; + } + decl->type = decl->type->ResolveUnboundVariability(Variability::Varying); + + if (d->declSpecs->storageClass != SC_NONE) + Error(decl->pos, "Storage class \"%s\" is illegal in " + "function parameter declaration for parameter \"%s\".", + lGetStorageClassName(d->declSpecs->storageClass), + decl->name.c_str()); + if (Type::Equal(decl->type, AtomicType::Void)) { + Error(decl->pos, "Parameter with type \"void\" illegal in function " + "parameter list."); + decl->type = NULL; + } + + const ArrayType *at = dynamic_cast(decl->type); if (at != NULL) { // As in C, arrays are passed to functions as pointers to // their element type. We'll just immediately make this @@ -459,69 +440,66 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { // report this differently than it was originally declared // in the function, but it's not clear that this is a // significant problem.) - if (at->GetElementType() == NULL) { + const Type *targetType = at->GetElementType(); + if (targetType == NULL) { Assert(m->errorCount > 0); - return NULL; + return; } - const Type *targetType = at->GetElementType(); - targetType = - targetType->ResolveUnboundVariability(Variability::Varying); - sym->type = PointerType::GetUniform(targetType); + decl->type = PointerType::GetUniform(targetType); // Make sure there are no unsized arrays (other than the // first dimension) in function parameter lists. - at = dynamic_cast(at->GetElementType()); + at = dynamic_cast(targetType); while (at != NULL) { if (at->GetElementCount() == 0) - Error(sym->pos, "Arrays with unsized dimensions in " + Error(decl->pos, "Arrays with unsized dimensions in " "dimensions after the first one are illegal in " "function parameter lists."); at = dynamic_cast(at->GetElementType()); } } - args.push_back(sym->type); - argNames.push_back(sym->name); - argPos.push_back(sym->pos); + args.push_back(decl->type); + argNames.push_back(decl->name); + argPos.push_back(decl->pos); Expr *init = NULL; - if (d->declarators.size()) { - // Try to find an initializer expression. - Declarator *decl = d->declarators[0]; - while (decl != NULL) { + // Try to find an initializer expression. + while (decl != NULL) { + if (decl->initExpr != NULL) { + decl->initExpr = TypeCheck(decl->initExpr); + decl->initExpr = Optimize(decl->initExpr); if (decl->initExpr != NULL) { - decl->initExpr = TypeCheck(decl->initExpr); - decl->initExpr = Optimize(decl->initExpr); - if (decl->initExpr != NULL) { - init = dynamic_cast(decl->initExpr); - if (init == NULL) - init = dynamic_cast(decl->initExpr); - if (init == NULL) - Error(decl->initExpr->pos, "Default value for parameter " - "\"%s\" must be a compile-time constant.", - sym->name.c_str()); - } - break; + init = dynamic_cast(decl->initExpr); + if (init == NULL) + init = dynamic_cast(decl->initExpr); + if (init == NULL) + Error(decl->initExpr->pos, "Default value for parameter " + "\"%s\" must be a compile-time constant.", + decl->name.c_str()); } - else - decl = decl->child; + break; } + else + decl = decl->child; } argDefaults.push_back(init); } - const Type *returnType = type; + const Type *returnType = baseType; if (returnType == NULL) { Error(pos, "No return type provided in function declaration."); - return NULL; + return; } if (dynamic_cast(returnType) != NULL) { Error(pos, "Illegal to return function type from function."); - return NULL; + return; } + returnType = returnType->ResolveUnboundVariability(Variability::Varying); + bool isExternC = ds && (ds->storageClass == SC_EXTERN_C); bool isExported = ds && ((ds->typeQualifiers & TYPEQUAL_EXPORT) != 0); bool isTask = ds && ((ds->typeQualifiers & TYPEQUAL_TASK) != 0); @@ -529,28 +507,27 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { if (isExported && isTask) { Error(pos, "Function can't have both \"task\" and \"export\" " "qualifiers"); - return NULL; + return; } if (isExternC && isTask) { Error(pos, "Function can't have both \"extern \"C\"\" and \"task\" " "qualifiers"); - return NULL; + return; } if (isExternC && isExported) { Error(pos, "Function can't have both \"extern \"C\"\" and \"export\" " "qualifiers"); - return NULL; + return; } if (child == NULL) { Assert(m->errorCount > 0); - return NULL; + return; } const FunctionType *functionType = new FunctionType(returnType, args, argNames, argDefaults, argPos, isTask, isExported, isExternC); - functionType = functionType->ResolveUnboundVariability(Variability::Varying); // handle any explicit __declspecs on the function if (ds != NULL) { @@ -572,60 +549,12 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { } } - return child->GetType(functionType, ds); - } - default: - FATAL("Unexpected decl kind"); - return NULL; + child->InitFromType(functionType, ds); + type = child->type; + name = child->name; } } - -const Type * -Declarator::GetType(DeclSpecs *ds) const { - const Type *baseType = ds->GetBaseType(pos); - const Type *type = GetType(baseType, ds); - - if (ds->declSpecList.size() > 0 && - type != NULL && - dynamic_cast(type) == NULL) { - Error(pos, "__declspec specifiers for non-function type \"%s\" are " - "not used.", type->GetString().c_str()); - } - - return type; -} - - -Symbol * -Declarator::GetSymbolForFunctionParameter(int paramNum) const { - Assert(paramNum < (int)functionParams.size()); - Declaration *d = functionParams[paramNum]; - - char buf[32]; - Symbol *sym; - if (d->declarators.size() == 0) { - // function declaration like foo(float), w/o a name for - // the parameter - sprintf(buf, "__anon_parameter_%d", paramNum); - sym = new Symbol(buf, pos); - sym->type = d->declSpecs->GetBaseType(pos); - } - else { - Assert(d->declarators.size() == 1); - sym = d->declarators[0]->GetSymbol(); - if (sym == NULL) { - // Handle more complex anonymous declarations like - // float (float **). - sprintf(buf, "__anon_parameter_%d", paramNum); - sym = new Symbol(buf, d->declarators[0]->pos); - sym->type = d->declarators[0]->GetType(d->declSpecs); - } - } - return sym; -} - - /////////////////////////////////////////////////////////////////////////// // Declaration @@ -655,27 +584,23 @@ Declaration::GetVariableDeclarations() const { for (unsigned int i = 0; i < declarators.size(); ++i) { Declarator *decl = declarators[i]; - if (decl == NULL) { + if (decl == NULL || decl->type == NULL) { // Ignore earlier errors Assert(m->errorCount > 0); continue; } - Symbol *sym = decl->GetSymbol(); - if (sym == NULL || sym->type == NULL) { - // Ignore errors - Assert(m->errorCount > 0); - continue; - } - sym->type = sym->type->ResolveUnboundVariability(Variability::Varying); - - if (Type::Equal(sym->type, AtomicType::Void)) - Error(sym->pos, "\"void\" type variable illegal in declaration."); - else if (dynamic_cast(sym->type) == NULL) { + if (Type::Equal(decl->type, AtomicType::Void)) + Error(decl->pos, "\"void\" type variable illegal in declaration."); + else if (dynamic_cast(decl->type) == NULL) { + decl->type = decl->type->ResolveUnboundVariability(Variability::Varying); + Symbol *sym = new Symbol(decl->name, decl->pos, decl->type, + decl->storageClass); m->symbolTable->AddVariable(sym); vars.push_back(VariableDeclaration(sym, decl->initExpr)); } } + return vars; } @@ -686,25 +611,20 @@ Declaration::DeclareFunctions() { for (unsigned int i = 0; i < declarators.size(); ++i) { Declarator *decl = declarators[i]; - if (decl == NULL) { + if (decl == NULL || decl->type == NULL) { // Ignore earlier errors Assert(m->errorCount > 0); continue; } - Symbol *sym = decl->GetSymbol(); - if (sym == NULL || sym->type == NULL) { - // Ignore errors - Assert(m->errorCount > 0); - continue; - } - sym->type = sym->type->ResolveUnboundVariability(Variability::Varying); - - if (dynamic_cast(sym->type) == NULL) + const FunctionType *ftype = + dynamic_cast(decl->type); + if (ftype == NULL) continue; bool isInline = (declSpecs->typeQualifiers & TYPEQUAL_INLINE); - m->AddFunctionDeclaration(sym, isInline); + m->AddFunctionDeclaration(decl->name, ftype, decl->storageClass, + isInline, decl->pos); } } @@ -718,6 +638,7 @@ Declaration::Print(int indent) const { declarators[i]->Print(indent+4); } + /////////////////////////////////////////////////////////////////////////// void @@ -748,21 +669,19 @@ GetStructTypesNamesPositions(const std::vector &sd, Declarator *d = (*sd[i]->declarators)[j]; d->InitFromDeclSpecs(&ds); - Symbol *sym = d->GetSymbol(); - - if (Type::Equal(sym->type, AtomicType::Void)) + if (Type::Equal(d->type, AtomicType::Void)) Error(d->pos, "\"void\" type illegal for struct member."); - elementTypes->push_back(sym->type); + elementTypes->push_back(d->type); - if (seenNames.find(sym->name) != seenNames.end()) + if (seenNames.find(d->name) != seenNames.end()) Error(d->pos, "Struct member \"%s\" has same name as a " - "previously-declared member.", sym->name.c_str()); + "previously-declared member.", d->name.c_str()); else - seenNames.insert(sym->name); + seenNames.insert(d->name); - elementNames->push_back(sym->name); - elementPositions->push_back(sym->pos); + elementNames->push_back(d->name); + elementPositions->push_back(d->pos); } } diff --git a/decl.h b/decl.h index ff96e149..ea2cb0fd 100644 --- a/decl.h +++ b/decl.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2010-2011, Intel Corporation + Copyright (c) 2010-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -47,8 +47,8 @@ variables--here, that the declaration has the 'static' and 'uniform' qualifiers, and that it's basic type is 'int'. Then for each variable declaration, the Declaraiton class holds an instance of a Declarator, - which in turn records the per-variable information like the symbol - name, array size (if any), initializer expression, etc. + which in turn records the per-variable information like the name, array + size (if any), initializer expression, etc. */ #ifndef ISPC_DECL_H @@ -61,15 +61,6 @@ struct VariableDeclaration; class Declaration; class Declarator; -enum StorageClass { - SC_NONE, - SC_EXTERN, - SC_STATIC, - SC_TYPEDEF, - SC_EXTERN_C -}; - - /* Multiple qualifiers can be provided with types in declarations; therefore, they are set up so that they can be ANDed together into an int. */ @@ -141,25 +132,11 @@ public: Declarator(DeclaratorKind dk, SourcePos p); /** Once a DeclSpecs instance is available, this method completes the - initialization of the Symbol, setting its Type accordingly. + initialization of the type member. */ void InitFromDeclSpecs(DeclSpecs *ds); - /** Get the actual type of the combination of Declarator and the given - DeclSpecs. If an explicit base type is provided, the declarator is - applied to that type; otherwise the base type from the DeclSpecs is - used. */ - const Type *GetType(DeclSpecs *ds) const; - const Type *GetType(const Type *base, DeclSpecs *ds) const; - - /** Returns the symbol corresponding to the function declared by this - declarator and symbols for its arguments in *args. */ - Symbol *GetFunctionInfo(DeclSpecs *ds, std::vector *args); - - Symbol *GetSymbolForFunctionParameter(int paramNum) const; - - /** Returns the symbol associated with the declarator. */ - Symbol *GetSymbol() const; + void InitFromType(const Type *base, DeclSpecs *ds); void Print(int indent) const; @@ -180,18 +157,24 @@ public: /** Type qualifiers provided with the declarator. */ int typeQualifiers; + StorageClass storageClass; + /** For array declarators, this gives the declared size of the array. Unsized arrays have arraySize == 0. */ int arraySize; - /** Symbol associated with the declarator. */ - Symbol *sym; + /** Name associated with the declarator. */ + std::string name; /** Initialization expression for the variable. May be NULL. */ Expr *initExpr; + /** Type of the declarator. This is NULL until InitFromDeclSpecs() or + InitFromType() is called. */ + const Type *type; + /** For function declarations, this holds the Declaration *s for the - funciton's parameters. */ + function's parameters. */ std::vector functionParams; }; diff --git a/func.cpp b/func.cpp index c1ca7ee6..d6e24d72 100644 --- a/func.cpp +++ b/func.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2011, Intel Corporation + Copyright (c) 2011-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -66,9 +66,8 @@ #include #include -Function::Function(Symbol *s, const std::vector &a, Stmt *c) { +Function::Function(Symbol *s, Stmt *c) { sym = s; - args = a; code = c; maskSymbol = m->symbolTable->LookupVariable("__mask"); @@ -104,9 +103,17 @@ Function::Function(Symbol *s, const std::vector &a, Stmt *c) { const FunctionType *type = dynamic_cast(sym->type); Assert(type != NULL); - for (unsigned int i = 0; i < args.size(); ++i) - if (dynamic_cast(args[i]->type) == NULL) - args[i]->parentFunction = this; + for (int i = 0; i < type->GetNumParameters(); ++i) { + const char *paramName = type->GetParameterName(i).c_str(); + Symbol *sym = m->symbolTable->LookupVariable(paramName); + if (sym == NULL) + Assert(strncmp(paramName, "__anon_parameter_", 17) == 0); + args.push_back(sym); + + const Type *t = type->GetParameterType(i); + if (sym != NULL && dynamic_cast(t) == NULL) + sym->parentFunction = this; + } if (type->isTask) { threadIndexSym = m->symbolTable->LookupVariable("threadIndex"); @@ -145,7 +152,8 @@ Function::GetType() const { 'mem2reg' pass will in turn promote to SSA registers.. */ static void -lCopyInTaskParameter(int i, llvm::Value *structArgPtr, const std::vector &args, +lCopyInTaskParameter(int i, llvm::Value *structArgPtr, const + std::vector &args, FunctionEmitContext *ctx) { // We expect the argument structure to come in as a poitner to a // structure. Confirm and figure out its type here. @@ -160,6 +168,10 @@ lCopyInTaskParameter(int i, llvm::Value *structArgPtr, const std::vectorgetElementType(i); Symbol *sym = args[i]; + if (sym == NULL) + // anonymous parameter, so don't worry about it + return; + // allocate space to copy the parameter in to sym->storagePtr = ctx->AllocaInst(argType, sym->name.c_str()); @@ -240,6 +252,10 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function, llvm::Function::arg_iterator argIter = function->arg_begin(); for (unsigned int i = 0; i < args.size(); ++i, ++argIter) { Symbol *sym = args[i]; + if (sym == NULL) + // anonymous function parameter + continue; + argIter->setName(sym->name.c_str()); // Allocate stack storage for the parameter and emit code diff --git a/func.h b/func.h index d0bf0731..6d0527fc 100644 --- a/func.h +++ b/func.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2011, Intel Corporation + Copyright (c) 2011-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -43,7 +43,7 @@ class Function { public: - Function(Symbol *sym, const std::vector &args, Stmt *code); + Function(Symbol *sym, Stmt *code); const Type *GetReturnType() const; const FunctionType *GetType() const; diff --git a/ispc.h b/ispc.h index 9e25baa0..b33cde02 100644 --- a/ispc.h +++ b/ispc.h @@ -116,6 +116,15 @@ class SymbolTable; class Type; struct VariableDeclaration; +enum StorageClass { + SC_NONE, + SC_EXTERN, + SC_STATIC, + SC_TYPEDEF, + SC_EXTERN_C +}; + + /** @brief Representation of a range of positions in a source file. This class represents a range of characters in a source file diff --git a/module.cpp b/module.cpp index 914a9bd2..d2d3afdd 100644 --- a/module.cpp +++ b/module.cpp @@ -231,64 +231,65 @@ Module::CompileFile() { void -Module::AddTypeDef(Symbol *sym) { +Module::AddTypeDef(const std::string &name, const Type *type, + SourcePos pos) { // Typedefs are easy; just add the mapping between the given name and // the given type. - symbolTable->AddType(sym->name.c_str(), sym->type, sym->pos); + symbolTable->AddType(name.c_str(), type, pos); } void -Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) { +Module::AddGlobalVariable(const std::string &name, const Type *type, Expr *initExpr, + bool isConst, StorageClass storageClass, SourcePos pos) { // These may be NULL due to errors in parsing; just gracefully return // here if so. - if (sym == NULL || sym->type == NULL) { - // But if these are NULL and there haven't been any previous - // errors, something surprising is going on + if (name == "" || type == NULL) { Assert(errorCount > 0); return; } - if (symbolTable->LookupFunction(sym->name.c_str())) { - Error(sym->pos, "Global variable \"%s\" shadows previously-declared " - "function.", sym->name.c_str()); + if (symbolTable->LookupFunction(name.c_str())) { + Error(pos, "Global variable \"%s\" shadows previously-declared " + "function.", name.c_str()); return; } - if (sym->storageClass == SC_EXTERN_C) { - Error(sym->pos, "extern \"C\" qualifier can only be used for " + if (storageClass == SC_EXTERN_C) { + Error(pos, "extern \"C\" qualifier can only be used for " "functions."); return; } - if (Type::Equal(sym->type, AtomicType::Void)) { - Error(sym->pos, "\"void\" type global variable is illegal."); + if (Type::Equal(type, AtomicType::Void)) { + Error(pos, "\"void\" type global variable is illegal."); return; } - sym->type = ArrayType::SizeUnsizedArrays(sym->type, initExpr); - if (sym->type == NULL) + type = ArrayType::SizeUnsizedArrays(type, initExpr); + if (type == NULL) return; - const ArrayType *at = dynamic_cast(sym->type); + const ArrayType *at = dynamic_cast(type); if (at != NULL && at->TotalElementCount() == 0) { - Error(sym->pos, "Illegal to declare a global variable with unsized " + Error(pos, "Illegal to declare a global variable with unsized " "array dimensions that aren't set with an initializer " "expression."); return; } - LLVM_TYPE_CONST llvm::Type *llvmType = sym->type->LLVMType(g->ctx); + LLVM_TYPE_CONST llvm::Type *llvmType = type->LLVMType(g->ctx); if (llvmType == NULL) return; // See if we have an initializer expression for the global. If so, // make sure it's a compile-time constant! llvm::Constant *llvmInitializer = NULL; - if (sym->storageClass == SC_EXTERN || sym->storageClass == SC_EXTERN_C) { + ConstExpr *constValue = NULL; + if (storageClass == SC_EXTERN || storageClass == SC_EXTERN_C) { if (initExpr != NULL) - Error(sym->pos, "Initializer can't be provided with \"extern\" " - "global variable \"%s\".", sym->name.c_str()); + Error(pos, "Initializer can't be provided with \"extern\" " + "global variable \"%s\".", name.c_str()); } else { if (initExpr != NULL) { @@ -299,27 +300,26 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) { // ExprList; they don't have types per se / can't type // convert themselves anyway.) if (dynamic_cast(initExpr) == NULL) - initExpr = TypeConvertExpr(initExpr, sym->type, "initializer"); + initExpr = TypeConvertExpr(initExpr, type, "initializer"); if (initExpr != NULL) { initExpr = Optimize(initExpr); // Fingers crossed, now let's see if we've got a // constant value.. - llvmInitializer = initExpr->GetConstant(sym->type); + llvmInitializer = initExpr->GetConstant(type); if (llvmInitializer != NULL) { - if (sym->type->IsConstType()) + if (type->IsConstType()) // Try to get a ConstExpr associated with // the symbol. This dynamic_cast can // validly fail, for example for types like // StructTypes where a ConstExpr can't // represent their values. - sym->constValue = - dynamic_cast(initExpr); + constValue = dynamic_cast(initExpr); } else Error(initExpr->pos, "Initializer for global variable \"%s\" " - "must be a constant.", sym->name.c_str()); + "must be a constant.", name.c_str()); } } } @@ -330,30 +330,33 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) { llvmInitializer = llvm::Constant::getNullValue(llvmType); } - Symbol *stSym = symbolTable->LookupVariable(sym->name.c_str()); + Symbol *sym = symbolTable->LookupVariable(name.c_str()); llvm::GlobalVariable *oldGV = NULL; - if (stSym != NULL) { + if (sym != NULL) { // We've already seen either a declaration or a definition of this // global. // If the type doesn't match with the previous one, issue an error. - if (!Type::Equal(sym->type, stSym->type)) { - Error(sym->pos, "Definition of variable \"%s\" conflicts with " - "definition at %s:%d.", sym->name.c_str(), - stSym->pos.name, stSym->pos.first_line); + if (!Type::Equal(sym->type, type) || + (sym->storageClass != SC_EXTERN && + sym->storageClass != SC_EXTERN_C && + sym->storageClass != storageClass)) { + Error(pos, "Definition of variable \"%s\" conflicts with " + "definition at %s:%d.", name.c_str(), + sym->pos.name, sym->pos.first_line); return; } llvm::GlobalVariable *gv = - llvm::dyn_cast(stSym->storagePtr); + llvm::dyn_cast(sym->storagePtr); Assert(gv != NULL); // And issue an error if this is a redefinition of a variable if (gv->hasInitializer() && sym->storageClass != SC_EXTERN && sym->storageClass != SC_EXTERN_C) { - Error(sym->pos, "Redefinition of variable \"%s\" is illegal. " + Error(pos, "Redefinition of variable \"%s\" is illegal. " "(Previous definition at %s:%d.)", sym->name.c_str(), - stSym->pos.name, stSym->pos.first_line); + sym->pos.name, sym->pos.first_line); return; } @@ -361,17 +364,12 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) { // of a previously-declared global. First, save the pointer to the // previous llvm::GlobalVariable oldGV = gv; - - // Now copy over all of the members of the current Symbol to the - // symbol in the symbol table. - *stSym = *sym; - // And copy the pointer of the one in the symbol table to sym, so - // that the operations below update storagePtr for the Symbol - // already in the symbol table. - sym = stSym; } - else + else { + sym = new Symbol(name, pos, type, storageClass); symbolTable->AddVariable(sym); + } + sym->constValue = constValue; llvm::GlobalValue::LinkageTypes linkage = (sym->storageClass == SC_STATIC) ? llvm::GlobalValue::InternalLinkage : @@ -393,10 +391,10 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) { } if (diBuilder) { - llvm::DIFile file = sym->pos.GetDIFile(); - diBuilder->createGlobalVariable(sym->name, + llvm::DIFile file = pos.GetDIFile(); + diBuilder->createGlobalVariable(name, file, - sym->pos.first_line, + pos.first_line, sym->type->GetDIType(file), (sym->storageClass == SC_STATIC), sym->storagePtr); @@ -487,22 +485,23 @@ lCheckForStructParameters(const FunctionType *ftype, SourcePos pos) { false if any errors were encountered. */ void -Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) { - const FunctionType *functionType = - dynamic_cast(funSym->type); +Module::AddFunctionDeclaration(const std::string &name, + const FunctionType *functionType, + StorageClass storageClass, bool isInline, + SourcePos pos) { Assert(functionType != NULL); // If a global variable with the same name has already been declared // issue an error. - if (symbolTable->LookupVariable(funSym->name.c_str()) != NULL) { - Error(funSym->pos, "Function \"%s\" shadows previously-declared global variable. " + if (symbolTable->LookupVariable(name.c_str()) != NULL) { + Error(pos, "Function \"%s\" shadows previously-declared global variable. " "Ignoring this definition.", - funSym->name.c_str()); + name.c_str()); return; } std::vector overloadFuncs; - symbolTable->LookupFunction(funSym->name.c_str(), &overloadFuncs); + symbolTable->LookupFunction(name.c_str(), &overloadFuncs); if (overloadFuncs.size() > 0) { for (unsigned int i = 0; i < overloadFuncs.size(); ++i) { Symbol *overloadFunc = overloadFuncs[i]; @@ -528,7 +527,7 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) { if (i == functionType->GetNumParameters()) { std::string thisRetType = functionType->GetReturnTypeString(); std::string otherRetType = ofType->GetReturnTypeString(); - Error(funSym->pos, "Illegal to overload function by return " + Error(pos, "Illegal to overload function by return " "type only. This function returns \"%s\" while " "previous declaration at %s:%d returns \"%s\".", thisRetType.c_str(), overloadFunc->pos.name, @@ -539,55 +538,54 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) { } } - if (funSym->storageClass == SC_EXTERN_C) { + if (storageClass == SC_EXTERN_C) { // Make sure the user hasn't supplied both an 'extern "C"' and a // 'task' qualifier with the function if (functionType->isTask) { - Error(funSym->pos, "\"task\" qualifier is illegal with C-linkage extern " - "function \"%s\". Ignoring this function.", funSym->name.c_str()); + Error(pos, "\"task\" qualifier is illegal with C-linkage extern " + "function \"%s\". Ignoring this function.", name.c_str()); return; } std::vector funcs; - symbolTable->LookupFunction(funSym->name.c_str(), &funcs); + symbolTable->LookupFunction(name.c_str(), &funcs); if (funcs.size() > 0) { if (funcs.size() > 1) { // Multiple functions with this name have already been declared; // can't overload here - Error(funSym->pos, "Can't overload extern \"C\" function \"%s\"; " + Error(pos, "Can't overload extern \"C\" function \"%s\"; " "%d functions with the same name have already been declared.", - funSym->name.c_str(), (int)funcs.size()); + name.c_str(), (int)funcs.size()); return; } // One function with the same name has been declared; see if it // has the same type as this one, in which case it's ok. - if (Type::Equal(funcs[0]->type, funSym->type)) + if (Type::Equal(funcs[0]->type, functionType)) return; else { - Error(funSym->pos, "Can't overload extern \"C\" function \"%s\".", - funSym->name.c_str()); + Error(pos, "Can't overload extern \"C\" function \"%s\".", + name.c_str()); return; } } } // Get the LLVM FunctionType - bool includeMask = (funSym->storageClass != SC_EXTERN_C); + bool includeMask = (storageClass != SC_EXTERN_C); LLVM_TYPE_CONST llvm::FunctionType *llvmFunctionType = functionType->LLVMFunctionType(g->ctx, includeMask); if (llvmFunctionType == NULL) return; // And create the llvm::Function - llvm::GlobalValue::LinkageTypes linkage = (funSym->storageClass == SC_STATIC || + llvm::GlobalValue::LinkageTypes linkage = (storageClass == SC_STATIC || isInline) ? llvm::GlobalValue::InternalLinkage : llvm::GlobalValue::ExternalLinkage; - std::string functionName; - if (funSym->storageClass == SC_EXTERN_C) - functionName = funSym->name; - else { - functionName = funSym->MangledName(); + + std::string functionName = name; + if (storageClass != SC_EXTERN_C) { + functionName += functionType->Mangle(); if (g->mangleFunctionsWithTarget) functionName += g->target.GetISAString(); } @@ -597,7 +595,7 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) { // Set function attributes: we never throw exceptions function->setDoesNotThrow(true); - if (!(funSym->storageClass == SC_EXTERN_C) && + if (storageClass != SC_EXTERN_C && !g->generateDebuggingSymbols && isInline) function->addFnAttr(llvm::Attribute::AlwaysInline); @@ -609,15 +607,15 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) { // 'export'ed. if (functionType->isExported && lRecursiveCheckValidParamType(functionType->GetReturnType())) - Error(funSym->pos, "Illegal to return a \"varying\" type from exported " - "function \"%s\"", funSym->name.c_str()); + Error(pos, "Illegal to return a \"varying\" type from exported " + "function \"%s\"", name.c_str()); if (functionType->isTask && Type::Equal(functionType->GetReturnType(), AtomicType::Void) == false) - Error(funSym->pos, "Task-qualified functions must have void return type."); + Error(pos, "Task-qualified functions must have void return type."); if (functionType->isExported || functionType->isExternC) - lCheckForStructParameters(functionType, funSym->pos); + lCheckForStructParameters(functionType, pos); // Loop over all of the arguments; process default values if present // and do other checks and parameter attribute setting. @@ -675,19 +673,33 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) { function->eraseFromParent(); function = module->getFunction(functionName); } - funSym->function = function; // Finally, we know all is good and we can add the function to the // symbol table + Symbol *funSym = new Symbol(name, pos, functionType, storageClass); + funSym->function = function; bool ok = symbolTable->AddFunction(funSym); Assert(ok); } void -Module::AddFunctionDefinition(Symbol *sym, const std::vector &args, +Module::AddFunctionDefinition(const std::string &name, const FunctionType *type, Stmt *code) { - ast->AddFunction(sym, args, code); + Symbol *sym = symbolTable->LookupFunction(name.c_str(), type); + if (sym == NULL) { + Assert(m->errorCount > 0); + return; + } + + // FIXME: because we encode the parameter names in the function type, + // we need to override the function type here in case the function had + // earlier been declared with anonymous parameter names but is now + // defined with actual names. This is yet another reason we shouldn't + // include the names in FunctionType... + sym->type = type; + + ast->AddFunction(sym, code); } diff --git a/module.h b/module.h index 9032548f..96231a5f 100644 --- a/module.h +++ b/module.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2010-2011, Intel Corporation + Copyright (c) 2010-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -59,21 +59,26 @@ public: int CompileFile(); /** Add a named type definition to the module. */ - void AddTypeDef(Symbol *sym); + void AddTypeDef(const std::string &name, const Type *type, + SourcePos pos); /** Add a new global variable corresponding to the given Symbol to the module. If non-NULL, initExpr gives the initiailizer expression for the global's inital value. */ - void AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst); + void AddGlobalVariable(const std::string &name, const Type *type, + Expr *initExpr, bool isConst, + StorageClass storageClass, SourcePos pos); /** Add a declaration of the function defined by the given function symbol to the module. */ - void AddFunctionDeclaration(Symbol *funSym, bool isInline); + void AddFunctionDeclaration(const std::string &name, + const FunctionType *ftype, + StorageClass sc, bool isInline, SourcePos pos); /** Adds the function described by the declaration information and the provided statements to the module. */ - void AddFunctionDefinition(Symbol *sym, const std::vector &args, - Stmt *code); + void AddFunctionDefinition(const std::string &name, + const FunctionType *ftype, Stmt *code); /** After a source file has been compiled, output can be generated in a number of different formats. */ diff --git a/parse.yy b/parse.yy index 8448a559..8a1e02ee 100644 --- a/parse.yy +++ b/parse.yy @@ -631,7 +631,9 @@ declaration_statement if ($1->declarators[i] == NULL) Assert(m->errorCount > 0); else - m->AddTypeDef($1->declarators[i]->GetSymbol()); + m->AddTypeDef($1->declarators[i]->name, + $1->declarators[i]->type, + $1->declarators[i]->pos); } $$ = NULL; } @@ -1174,7 +1176,7 @@ direct_declarator : TOKEN_IDENTIFIER { Declarator *d = new Declarator(DK_BASE, @1); - d->sym = new Symbol(yytext, @1); + d->name = yytext; $$ = d; } | '(' declarator ')' @@ -1349,8 +1351,10 @@ type_name { if ($1 == NULL || $2 == NULL) $$ = NULL; - else - $$ = $2->GetType($1, NULL); + else { + $2->InitFromType($1, NULL); + $$ = $2->type; + } } ; @@ -1854,11 +1858,14 @@ function_definition } compound_statement { - std::vector args; if ($2 != NULL) { - Symbol *sym = $2->GetFunctionInfo($1, &args); - if (sym != NULL) - m->AddFunctionDefinition(sym, args, $4); + $2->InitFromDeclSpecs($1); + const FunctionType *funcType = + dynamic_cast($2->type); + if (funcType == NULL) + Assert(m->errorCount > 0); + else + m->AddFunctionDefinition($2->name, funcType, $4); } m->symbolTable->PopScope(); // push in lAddFunctionParams(); } @@ -1968,35 +1975,27 @@ lAddDeclaration(DeclSpecs *ds, Declarator *decl) { // Error happened earlier during parsing return; + decl->InitFromDeclSpecs(ds); if (ds->storageClass == SC_TYPEDEF) - m->AddTypeDef(decl->GetSymbol()); + m->AddTypeDef(decl->name, decl->type, decl->pos); else { - const Type *t = decl->GetType(ds); - if (t == NULL) { + if (decl->type == NULL) { Assert(m->errorCount > 0); return; } - Symbol *sym = decl->GetSymbol(); - if (sym == NULL) { - Assert(m->errorCount > 0); - return; - } - - const FunctionType *ft = dynamic_cast(t); + decl->type = decl->type->ResolveUnboundVariability(Variability::Varying); + + const FunctionType *ft = dynamic_cast(decl->type); if (ft != NULL) { - sym->type = ft; - sym->storageClass = ds->storageClass; bool isInline = (ds->typeQualifiers & TYPEQUAL_INLINE); - m->AddFunctionDeclaration(sym, isInline); + m->AddFunctionDeclaration(decl->name, ft, ds->storageClass, + isInline, decl->pos); } else { - if (sym->type == NULL) - Assert(m->errorCount > 0); - else - sym->type = sym->type->ResolveUnboundVariability(Variability::Varying); bool isConst = (ds->typeQualifiers & TYPEQUAL_CONST) != 0; - m->AddGlobalVariable(sym, decl->initExpr, isConst); + m->AddGlobalVariable(decl->name, decl->type, decl->initExpr, + isConst, decl->storageClass, decl->pos); } } } @@ -2025,16 +2024,13 @@ lAddFunctionParams(Declarator *decl) { // now loop over its parameters and add them to the symbol table for (unsigned int i = 0; i < decl->functionParams.size(); ++i) { Declaration *pdecl = decl->functionParams[i]; - if (pdecl == NULL || pdecl->declarators.size() == 0) - // zero size declarators array corresponds to an anonymous - // parameter - continue; - Assert(pdecl->declarators.size() == 1); - Symbol *sym = pdecl->declarators[0]->GetSymbol(); - if (sym == NULL || sym->type == NULL) + Assert(pdecl != NULL && pdecl->declarators.size() == 1); + Declarator *declarator = pdecl->declarators[0]; + if (declarator == NULL) Assert(m->errorCount > 0); else { - sym->type = sym->type->ResolveUnboundVariability(Variability::Varying); + Symbol *sym = new Symbol(declarator->name, declarator->pos, + declarator->type, declarator->storageClass); #ifndef NDEBUG bool ok = m->symbolTable->AddVariable(sym); if (ok == false) diff --git a/sym.cpp b/sym.cpp index f60dc1aa..1a503c91 100644 --- a/sym.cpp +++ b/sym.cpp @@ -56,12 +56,6 @@ Symbol::Symbol(const std::string &n, SourcePos p, const Type *t, } -std::string -Symbol::MangledName() const { - return name + type->Mangle(); -} - - /////////////////////////////////////////////////////////////////////////// // SymbolTable diff --git a/sym.h b/sym.h index 8e14495a..24eb810f 100644 --- a/sym.h +++ b/sym.h @@ -67,13 +67,6 @@ public: Symbol(const std::string &name, SourcePos pos, const Type *t = NULL, StorageClass sc = SC_NONE); - /** This method should only be called for function symbols; for them, - it returns a mangled version of the function name with the argument - types encoded into the returned name. This is used to generate - unique symbols in object files for overloaded functions. - */ - std::string MangledName() const; - SourcePos pos; /*!< Source file position where the symbol was defined */ std::string name; /*!< Symbol's name */ llvm::Value *storagePtr; /*!< For symbols with storage associated with diff --git a/tests/func-anon-param.ispc b/tests/func-anon-param.ispc new file mode 100644 index 00000000..8bf97065 --- /dev/null +++ b/tests/func-anon-param.ispc @@ -0,0 +1,15 @@ + + +export uniform int width() { return programCount; } + +float foo(float &) { return 1; } +float bar(uniform float []) { return 2; } + +export void f_f(uniform float RET[], uniform float aFOO[]) { + float x = 0; + RET[programIndex] = foo(x) + bar(aFOO); +} + +export void result(uniform float RET[]) { + RET[programIndex] = 3; +} diff --git a/tests/global-decl-define.ispc b/tests/global-decl-define.ispc new file mode 100644 index 00000000..44fb92a7 --- /dev/null +++ b/tests/global-decl-define.ispc @@ -0,0 +1,14 @@ + + +export uniform int width() { return programCount; } + +extern int foo; +int foo = 1; + +export void f_f(uniform float RET[], uniform float aFOO[]) { + RET[programIndex] = foo; +} + +export void result(uniform float RET[]) { + RET[programIndex] = 1; +} diff --git a/tests/ptr-cast-complex.ispc b/tests/ptr-cast-complex.ispc new file mode 100644 index 00000000..afdbf5e7 --- /dev/null +++ b/tests/ptr-cast-complex.ispc @@ -0,0 +1,18 @@ + +export uniform int width() { return programCount; } + +export void f_f(uniform float RET[], uniform float aFOO[]) { + uniform int x[2][10]; + for (uniform int i = 0; i < 2; ++i) { + for (uniform int j = 0; j < 10; ++j) { + x[i][j] = 10*i+j; + } + } + + uniform int (* varying y)[10] = x; + RET[programIndex] = y[1][programIndex % 5]; +} + +export void result(uniform float RET[]) { + RET[programIndex] = 10+ (programIndex % 5); +}