void DeclSpecs::Print() const { printf("Declspecs: [%s ", lGetStorageClassName(storageClass)); if (soaWidth > 0) printf("soa<%d> ", soaWidth); lPrintTypeQualifiers(typeQualifiers); printf("base type: %s", baseType->GetString().c_str()); if (vectorSize > 0) printf("<%d>", vectorSize); printf("]"); }
void DeclSpecs::Print() const { printf("%s ", lGetStorageClassName(storageClass)); if (soaWidth > 0) printf("soa<%d> ", soaWidth); if (typeQualifiers & TYPEQUAL_INLINE) printf("inline "); if (typeQualifiers & TYPEQUAL_CONST) printf("const "); if (typeQualifiers & TYPEQUAL_UNIFORM) printf("uniform "); if (typeQualifiers & TYPEQUAL_VARYING) printf("varying "); if (typeQualifiers & TYPEQUAL_TASK) printf("task "); if (typeQualifiers & TYPEQUAL_SIGNED) printf("signed "); if (typeQualifiers & TYPEQUAL_UNSIGNED) printf("unsigned "); printf("%s", baseType->GetString().c_str()); if (vectorSize > 0) printf("<%d>", vectorSize); }
void Declarator::Print(int indent) const { printf("%*cdeclarator: [", indent, ' '); pos.Print(); lPrintTypeQualifiers(typeQualifiers); printf("%s ", lGetStorageClassName(storageClass)); if (name.size() > 0) printf("%s", name.c_str()); else printf("(unnamed)"); printf(", array size = %d", arraySize); printf(", kind = "); switch (kind) { case DK_BASE: printf("base"); break; case DK_POINTER: printf("pointer"); break; case DK_REFERENCE: printf("reference"); break; case DK_ARRAY: printf("array"); break; case DK_FUNCTION: printf("function"); break; default: FATAL("Unhandled declarator kind"); } if (initExpr != NULL) { printf(" = ("); initExpr->Print(); printf(")"); } if (functionParams.size() > 0) { for (unsigned int i = 0; i < functionParams.size(); ++i) { printf("\n%*cfunc param %d:\n", indent, ' ', i); functionParams[i]->Print(indent+4); } } if (child != NULL) child->Print(indent + 4); printf("]\n"); }
const Type * Declarator::GetType(const Type *base, DeclSpecs *ds) const { bool hasUniformQual = ((typeQualifiers & TYPEQUAL_UNIFORM) != 0); bool hasVaryingQual = ((typeQualifiers & TYPEQUAL_VARYING) != 0); bool isTask = ((typeQualifiers & TYPEQUAL_TASK) != 0); bool isConst = ((typeQualifiers & TYPEQUAL_CONST) != 0); if (hasUniformQual && hasVaryingQual) { Error(pos, "Can't provide both \"uniform\" and \"varying\" qualifiers."); return NULL; } if (kind != DK_FUNCTION && isTask) Error(pos, "\"task\" qualifier illegal in variable declaration."); const Type *type = base; switch (kind) { case 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 = new PointerType(type, hasUniformQual, isConst); if (child != NULL) return child->GetType(type, ds); else return type; break; case DK_REFERENCE: if (hasUniformQual) Error(pos, "\"uniform\" qualifier is illegal to apply to references."); if (hasVaryingQual) Error(pos, "\"varying\" qualifier is illegal to apply to references."); if (isConst) Error(pos, "\"const\" qualifier is to illegal apply to references."); // The parser should disallow this already, but double check. if (dynamic_cast<const ReferenceType *>(type) != NULL) { Error(pos, "References to references are illegal."); return NULL; } type = new ReferenceType(type); if (child != NULL) return child->GetType(type, ds); else return type; break; case DK_ARRAY: type = new ArrayType(type, arraySize); if (child) return child->GetType(type, ds); else return type; break; case DK_FUNCTION: { std::vector<const Type *> args; std::vector<std::string> argNames; std::vector<ConstExpr *> argDefaults; std::vector<SourcePos> argPos; // Loop over the function arguments and store the names, types, // default values (if any), and source file positions each one in // the corresponding vector. for (unsigned int i = 0; i < functionParams.size(); ++i) { Declaration *d = functionParams[i]; 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", i); sym = new Symbol(buf, pos); sym->type = d->declSpecs->GetBaseType(pos); } else { sym = d->declarators[0]->GetSymbol(); if (sym == NULL) { // Handle more complex anonymous declarations like // float (float **). sprintf(buf, "__anon_parameter_%d", i); sym = new Symbol(buf, d->declarators[0]->pos); sym->type = d->declarators[0]->GetType(d->declSpecs); } } 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()); const ArrayType *at = dynamic_cast<const ArrayType *>(sym->type); if (at != NULL) { // As in C, arrays are passed to functions as pointers to // their element type. We'll just immediately make this // change now. (One shortcoming of losing the fact that // the it was originally an array is that any warnings or // errors later issued that print the function type will // report this differently than it was originally declared // in the function, but it's not clear that this is a // significant problem.) sym->type = PointerType::GetUniform(at->GetElementType()); // Make sure there are no unsized arrays (other than the // first dimension) in function parameter lists. at = dynamic_cast<const ArrayType *>(at->GetElementType()); while (at != NULL) { if (at->GetElementCount() == 0) Error(sym->pos, "Arrays with unsized dimensions in " "dimensions after the first one are illegal in " "function parameter lists."); at = dynamic_cast<const ArrayType *>(at->GetElementType()); } } args.push_back(sym->type); argNames.push_back(sym->name); argPos.push_back(sym->pos); ConstExpr *init = NULL; if (d->declarators.size()) { // Try to find an initializer expression; if there is one, // it lives down to the base declarator. Declarator *decl = d->declarators[0]; while (decl->child != NULL) { Assert(decl->initExpr == NULL); decl = decl->child; } if (decl->initExpr != NULL && (decl->initExpr = TypeCheck(decl->initExpr)) != NULL && (decl->initExpr = Optimize(decl->initExpr)) != NULL && (init = dynamic_cast<ConstExpr *>(decl->initExpr)) == NULL) { Error(decl->initExpr->pos, "Default value for parameter " "\"%s\" must be a compile-time constant.", sym->name.c_str()); } } argDefaults.push_back(init); } const Type *returnType = type; if (returnType == NULL) { Error(pos, "No return type provided in function declaration."); return NULL; } bool isExported = ds && (ds->storageClass == SC_EXPORT); bool isExternC = ds && (ds->storageClass == SC_EXTERN_C); bool isTask = ds && ((ds->typeQualifiers & TYPEQUAL_TASK) != 0); if (isExported && isTask) { Error(pos, "Function can't have both \"task\" and \"export\" " "qualifiers"); return NULL; } if (isExternC && isTask) { Error(pos, "Function can't have both \"extern \"C\"\" and \"task\" " "qualifiers"); return NULL; } if (isExternC && isExported) { Error(pos, "Function can't have both \"extern \"C\"\" and \"export\" " "qualifiers"); return NULL; } Type *functionType = new FunctionType(returnType, args, pos, argNames, argDefaults, argPos, isTask, isExported, isExternC); return child->GetType(functionType, ds); } default: FATAL("Unexpected decl kind"); return NULL; } #if 0 // Make sure we actually have an array of structs .. const StructType *childStructType = dynamic_cast<const StructType *>(childType); if (childStructType == NULL) { Error(pos, "Illegal to provide soa<%d> qualifier with non-struct " "type \"%s\".", soaWidth, childType->GetString().c_str()); return new ArrayType(childType, arraySize == -1 ? 0 : arraySize); } else if ((soaWidth & (soaWidth - 1)) != 0) { Error(pos, "soa<%d> width illegal. Value must be power of two.", soaWidth); return NULL; } else if (arraySize != -1 && (arraySize % soaWidth) != 0) { Error(pos, "soa<%d> width must evenly divide array size %d.", soaWidth, arraySize); return NULL; } return new SOAArrayType(childStructType, arraySize == -1 ? 0 : arraySize, soaWidth); #endif }
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); bool isExported = ((typeQualifiers & TYPEQUAL_EXPORT) != 0); bool isConst = ((typeQualifiers & TYPEQUAL_CONST) != 0); bool isUnmasked = ((typeQualifiers & TYPEQUAL_UNMASKED) != 0); if (hasUniformQual && hasVaryingQual) { Error(pos, "Can't provide both \"uniform\" and \"varying\" qualifiers."); return; } if (kind != DK_FUNCTION && isTask) { Error(pos, "\"task\" qualifier illegal in variable declaration."); return; } if (kind != DK_FUNCTION && isUnmasked) { Error(pos, "\"unmasked\" qualifier illegal in variable declaration."); return; } if (kind != DK_FUNCTION && isExported) { Error(pos, "\"export\" qualifier illegal in variable declaration."); return; } Variability variability(Variability::Unbound); if (hasUniformQual) variability = Variability::Uniform; else if (hasVaryingQual) variability = Variability::Varying; if (kind == DK_BASE) { // All of the type qualifiers should be in the DeclSpecs for the // base declarator AssertPos(pos, typeQualifiers == 0); AssertPos(pos, child == NULL); 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. */ const Type *ptrType = new PointerType(baseType, variability, isConst, baseType->IsSOAType()); if (child != NULL) { child->InitFromType(ptrType, ds); type = child->type; name = child->name; } else type = ptrType; } else if (kind == DK_REFERENCE) { if (hasUniformQual) { Error(pos, "\"uniform\" qualifier is illegal to apply to references."); return; } if (hasVaryingQual) { Error(pos, "\"varying\" qualifier is illegal to apply to references."); return; } if (isConst) { Error(pos, "\"const\" qualifier is to illegal apply to references."); return; } // The parser should disallow this already, but double check. if (CastType<ReferenceType>(baseType) != NULL) { Error(pos, "References to references are illegal."); return; } const Type *refType = new ReferenceType(baseType); if (child != NULL) { child->InitFromType(refType, ds); type = child->type; name = child->name; } else type = refType; } else if (kind == DK_ARRAY) { if (Type::Equal(baseType, AtomicType::Void)) { Error(pos, "Arrays of \"void\" type are illegal."); return; } if (CastType<ReferenceType>(baseType)) { Error(pos, "Arrays of references (type \"%s\") are illegal.", baseType->GetString().c_str()); return; } const Type *arrayType = new ArrayType(baseType, arraySize); if (child != NULL) { child->InitFromType(arrayType, ds); type = child->type; name = child->name; } else type = arrayType; } else if (kind == DK_FUNCTION) { llvm::SmallVector<const Type *, 8> args; llvm::SmallVector<std::string, 8> argNames; llvm::SmallVector<Expr *, 8> argDefaults; llvm::SmallVector<SourcePos, 8> argPos; // Loop over the function arguments and store the names, types, // default values (if any), and source file positions each one in // the corresponding vector. for (unsigned int i = 0; i < functionParams.size(); ++i) { Declaration *d = functionParams[i]; if (d == NULL) { AssertPos(pos, 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); } AssertPos(pos, d->declarators.size() == 1); Declarator *decl = d->declarators[0]; if (decl == NULL || decl->type == NULL) { AssertPos(pos, m->errorCount > 0); continue; } 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 = CastType<ArrayType>(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 // change now. (One shortcoming of losing the fact that // the it was originally an array is that any warnings or // errors later issued that print the function type will // report this differently than it was originally declared // in the function, but it's not clear that this is a // significant problem.) const Type *targetType = at->GetElementType(); if (targetType == NULL) { AssertPos(pos, m->errorCount > 0); return; } decl->type = PointerType::GetUniform(targetType); // Make sure there are no unsized arrays (other than the // first dimension) in function parameter lists. at = CastType<ArrayType>(targetType); while (at != NULL) { if (at->GetElementCount() == 0) Error(decl->pos, "Arrays with unsized dimensions in " "dimensions after the first one are illegal in " "function parameter lists."); at = CastType<ArrayType>(at->GetElementType()); } } args.push_back(decl->type); argNames.push_back(decl->name); argPos.push_back(decl->pos); Expr *init = 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) { init = dynamic_cast<ConstExpr *>(decl->initExpr); if (init == NULL) init = dynamic_cast<NullPointerExpr *>(decl->initExpr); if (init == NULL) Error(decl->initExpr->pos, "Default value for parameter " "\"%s\" must be a compile-time constant.", decl->name.c_str()); } break; } else decl = decl->child; } argDefaults.push_back(init); } const Type *returnType = baseType; if (returnType == NULL) { Error(pos, "No return type provided in function declaration."); return; } if (CastType<FunctionType>(returnType) != NULL) { Error(pos, "Illegal to return function type from function."); 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); bool isUnmasked = ds && ((ds->typeQualifiers & TYPEQUAL_UNMASKED) != 0); if (isExported && isTask) { Error(pos, "Function can't have both \"task\" and \"export\" " "qualifiers"); return; } if (isExternC && isTask) { Error(pos, "Function can't have both \"extern \"C\"\" and \"task\" " "qualifiers"); return; } if (isExternC && isExported) { Error(pos, "Function can't have both \"extern \"C\"\" and \"export\" " "qualifiers"); return; } if (isUnmasked && isExported) Warning(pos, "\"unmasked\" qualifier is redundant for exported " "functions."); if (child == NULL) { AssertPos(pos, m->errorCount > 0); return; } const FunctionType *functionType = new FunctionType(returnType, args, argNames, argDefaults, argPos, isTask, isExported, isExternC, isUnmasked); // handle any explicit __declspecs on the function if (ds != NULL) { for (int i = 0; i < (int)ds->declSpecList.size(); ++i) { std::string str = ds->declSpecList[i].first; SourcePos pos = ds->declSpecList[i].second; if (str == "safe") (const_cast<FunctionType *>(functionType))->isSafe = true; else if (!strncmp(str.c_str(), "cost", 4)) { int cost = atoi(str.c_str() + 4); if (cost < 0) Error(pos, "Negative function cost %d is illegal.", cost); (const_cast<FunctionType *>(functionType))->costOverride = cost; } else Error(pos, "__declspec parameter \"%s\" unknown.", str.c_str()); } } child->InitFromType(functionType, ds); type = child->type; name = child->name; } }