Pointer fixes/improvements.

Allow <, <=, >, >= comparisons of pointers
Allow explicit type-casting of pointers to and from integers
Fix bug in handling expressions of the form "int + ptr" ("ptr + int"
  was fine).
Fix a bug in TypeCastExpr where varying -> uniform typecasts
  would be allowed (leading to a crash later)
This commit is contained in:
Matt Pharr
2011-11-29 13:22:36 -08:00
parent 4ca90272ba
commit e52104ff55
15 changed files with 235 additions and 40 deletions

53
ctx.cpp
View File

@@ -1156,19 +1156,62 @@ FunctionEmitContext::PtrToIntInst(llvm::Value *value, const char *name) {
llvm::Value *
FunctionEmitContext::IntToPtrInst(llvm::Value *value, LLVM_TYPE_CONST llvm::Type *type,
FunctionEmitContext::PtrToIntInst(llvm::Value *value,
LLVM_TYPE_CONST llvm::Type *toType,
const char *name) {
if (value == NULL) {
assert(m->errorCount > 0);
return NULL;
}
if (llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(value->getType()))
// no-op for varying pointers; they're already vectors of ints
return value;
LLVM_TYPE_CONST llvm::Type *fromType = value->getType();
if (llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(fromType)) {
// varying pointer
if (fromType == toType)
// already the right type--done
return value;
else if (fromType->getScalarSizeInBits() > toType->getScalarSizeInBits())
return TruncInst(value, toType, "ptr_to_int");
else {
assert(fromType->getScalarSizeInBits() <
toType->getScalarSizeInBits());
return ZExtInst(value, toType, "ptr_to_int");
}
}
llvm::Instruction *inst =
new llvm::IntToPtrInst(value, type, name ? name : "int2ptr", bblock);
new llvm::PtrToIntInst(value, toType, name ? name : "ptr2int", bblock);
AddDebugPos(inst);
return inst;
}
llvm::Value *
FunctionEmitContext::IntToPtrInst(llvm::Value *value,
LLVM_TYPE_CONST llvm::Type *toType,
const char *name) {
if (value == NULL) {
assert(m->errorCount > 0);
return NULL;
}
LLVM_TYPE_CONST llvm::Type *fromType = value->getType();
if (llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(fromType)) {
// varying pointer
if (fromType == toType)
// done
return value;
else if (fromType->getScalarSizeInBits() > toType->getScalarSizeInBits())
return TruncInst(value, toType, "int_to_ptr");
else {
assert(fromType->getScalarSizeInBits() <
toType->getScalarSizeInBits());
return ZExtInst(value, toType, "int_to_ptr");
}
}
llvm::Instruction *inst =
new llvm::IntToPtrInst(value, toType, name ? name : "int2ptr", bblock);
AddDebugPos(inst);
return inst;
}

4
ctx.h
View File

@@ -41,9 +41,7 @@
#include "ispc.h"
#include <llvm/InstrTypes.h>
#include <llvm/Instructions.h>
#ifndef LLVM_2_8
#include <llvm/Analysis/DIBuilder.h>
#endif
#include <llvm/Analysis/DebugInfo.h>
struct CFInfo;
@@ -316,6 +314,8 @@ public:
llvm::Value *BitCastInst(llvm::Value *value, LLVM_TYPE_CONST llvm::Type *type,
const char *name = NULL);
llvm::Value *PtrToIntInst(llvm::Value *value, const char *name = NULL);
llvm::Value *PtrToIntInst(llvm::Value *value, LLVM_TYPE_CONST llvm::Type *type,
const char *name = NULL);
llvm::Value *IntToPtrInst(llvm::Value *value, LLVM_TYPE_CONST llvm::Type *type,
const char *name = NULL);

123
expr.cpp
View File

@@ -1294,8 +1294,9 @@ BinaryExpr::GetType() const {
// ptr - int -> ptr
return type0;
}
// otherwise fall through for these two...
assert(op == Equal || op == NotEqual);
// otherwise fall through for these...
assert(op == Lt || op == Gt || op == Le || op == Ge ||
op == Equal || op == NotEqual);
}
const Type *exprType = Type::MoreGeneralType(type0, type1, pos, lOpString(op));
@@ -1648,6 +1649,7 @@ BinaryExpr::TypeCheck() {
// put in canonical order with the pointer as the first operand
// for GetValue()
std::swap(arg0, arg1);
std::swap(type0, type1);
std::swap(pt0, pt1);
}
@@ -1726,11 +1728,7 @@ BinaryExpr::TypeCheck() {
case Sub:
case Mul:
case Div:
case Mod:
case Lt:
case Gt:
case Le:
case Ge: {
case Mod: {
// Must be numeric type for these. (And mod is special--can't be float)
if (!type0->IsNumericType() || (op == Mod && type0->IsFloatType())) {
Error(arg0->pos, "First operand to binary operator \"%s\" is of "
@@ -1756,6 +1754,10 @@ BinaryExpr::TypeCheck() {
return NULL;
return this;
}
case Lt:
case Gt:
case Le:
case Ge:
case Equal:
case NotEqual: {
const PointerType *pt0 = dynamic_cast<const PointerType *>(type0);
@@ -1763,14 +1765,14 @@ BinaryExpr::TypeCheck() {
if (pt0 == NULL && pt1 == NULL) {
if (!type0->IsBoolType() && !type0->IsNumericType()) {
Error(arg0->pos,
"First operand to equality operator \"%s\" is of "
"First operand to operator \"%s\" is of "
"non-comparable type \"%s\".", lOpString(op),
type0->GetString().c_str());
return NULL;
}
if (!type1->IsBoolType() && !type1->IsNumericType()) {
Error(arg1->pos,
"Second operand to equality operator \"%s\" is of "
"Second operand to operator \"%s\" is of "
"non-comparable type \"%s\".", lOpString(op),
type1->GetString().c_str());
return NULL;
@@ -5132,30 +5134,46 @@ TypeCastExpr::GetValue(FunctionEmitContext *ctx) const {
}
}
else {
// convert pointer to bool
assert(dynamic_cast<const AtomicType *>(toType) &&
toType->IsBoolType());
LLVM_TYPE_CONST llvm::Type *lfu =
fromType->GetAsUniformType()->LLVMType(g->ctx);
LLVM_TYPE_CONST llvm::PointerType *llvmFromUnifType =
llvm::dyn_cast<LLVM_TYPE_CONST llvm::PointerType>(lfu);
assert(dynamic_cast<const AtomicType *>(toType) != NULL);
if (toType->IsBoolType()) {
// convert pointer to bool
LLVM_TYPE_CONST llvm::Type *lfu =
fromType->GetAsUniformType()->LLVMType(g->ctx);
LLVM_TYPE_CONST llvm::PointerType *llvmFromUnifType =
llvm::dyn_cast<LLVM_TYPE_CONST llvm::PointerType>(lfu);
llvm::Value *nullPtrValue = llvm::ConstantPointerNull::get(llvmFromUnifType);
if (fromType->IsVaryingType())
nullPtrValue = ctx->SmearUniform(nullPtrValue);
llvm::Value *nullPtrValue =
llvm::ConstantPointerNull::get(llvmFromUnifType);
if (fromType->IsVaryingType())
nullPtrValue = ctx->SmearUniform(nullPtrValue);
llvm::Value *exprVal = expr->GetValue(ctx);
llvm::Value *cmp = ctx->CmpInst(llvm::Instruction::ICmp,
llvm::CmpInst::ICMP_NE,
exprVal, nullPtrValue, "ptr_ne_NULL");
llvm::Value *exprVal = expr->GetValue(ctx);
llvm::Value *cmp =
ctx->CmpInst(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_NE,
exprVal, nullPtrValue, "ptr_ne_NULL");
if (toType->IsVaryingType()) {
if (fromType->IsUniformType())
cmp = ctx->SmearUniform(cmp);
cmp = ctx->I1VecToBoolVec(cmp);
if (toType->IsVaryingType()) {
if (fromType->IsUniformType())
cmp = ctx->SmearUniform(cmp);
cmp = ctx->I1VecToBoolVec(cmp);
}
return cmp;
}
else {
// ptr -> int
llvm::Value *value = expr->GetValue(ctx);
if (value == NULL)
return NULL;
return cmp;
if (toType->IsVaryingType() && fromType->IsUniformType())
value = ctx->SmearUniform(value);
LLVM_TYPE_CONST llvm::Type *llvmToType = toType->LLVMType(g->ctx);
if (llvmToType == NULL)
return NULL;
return ctx->PtrToIntInst(value, llvmToType, "ptr_typecast");
}
}
}
@@ -5304,6 +5322,17 @@ TypeCastExpr::GetValue(FunctionEmitContext *ctx) const {
cast = ctx->InsertInst(cast, conv, i);
return cast;
}
else if (toPointerType != NULL) {
// int -> ptr
if (toType->IsVaryingType() && fromType->IsUniformType())
exprVal = ctx->SmearUniform(exprVal);
LLVM_TYPE_CONST llvm::Type *llvmToType = toType->LLVMType(g->ctx);
if (llvmToType == NULL)
return NULL;
return ctx->IntToPtrInst(exprVal, llvmToType, "int_to_ptr");
}
else {
const AtomicType *toAtomic = dynamic_cast<const AtomicType *>(toType);
// typechecking should ensure this is the case
@@ -5352,10 +5381,17 @@ TypeCastExpr::TypeCheck() {
fromType = lDeconstifyType(fromType);
toType = lDeconstifyType(toType);
if (fromType->IsVaryingType() && toType->IsUniformType()) {
Error(pos, "Can't type cast from varying type \"%s\" to uniform "
"type \"%s\"", fromType->GetString().c_str(),
toType->GetString().c_str());
return NULL;
}
// First some special cases that we allow only with an explicit type cast
const PointerType *ptFrom = dynamic_cast<const PointerType *>(fromType);
const PointerType *ptTo = dynamic_cast<const PointerType *>(toType);
if (ptFrom != NULL && ptTo != NULL)
const PointerType *fromPtr = dynamic_cast<const PointerType *>(fromType);
const PointerType *toPtr = dynamic_cast<const PointerType *>(toType);
if (fromPtr != NULL && toPtr != NULL)
// allow explicit typecasts between any two different pointer types
return this;
@@ -5367,6 +5403,25 @@ TypeCastExpr::TypeCheck() {
// Allow explicit casts between all of these
return this;
// ptr -> int type casts
if (fromPtr != NULL && toAtomic != NULL && toAtomic->IsIntType()) {
bool safeCast = (toAtomic->basicType == AtomicType::TYPE_INT64 ||
toAtomic->basicType == AtomicType::TYPE_UINT64);
if (g->target.is32Bit)
safeCast |= (toAtomic->basicType == AtomicType::TYPE_INT32 ||
toAtomic->basicType == AtomicType::TYPE_UINT32);
if (safeCast == false)
Warning(pos, "Pointer type cast of type \"%s\" to integer type "
"\"%s\" may lose information.",
fromType->GetString().c_str(),
toType->GetString().c_str());
return this;
}
// int -> ptr
if (fromAtomic != NULL && fromAtomic->IsIntType() && toPtr != NULL)
return this;
// And otherwise see if it's one of the conversions allowed to happen
// implicitly.
if (CanConvertTypes(fromType, toType, "type cast expression", pos) == false)
@@ -5483,6 +5538,12 @@ TypeCastExpr::Print() const {
}
Symbol *
TypeCastExpr::GetBaseSymbol() const {
return expr ? expr->GetBaseSymbol() : NULL;
}
llvm::Constant *
TypeCastExpr::GetConstant(const Type *constType) const {
// We don't need to worry about most the basic cases where the type

1
expr.h
View File

@@ -502,6 +502,7 @@ public:
Expr *TypeCheck();
Expr *Optimize();
int EstimateCost() const;
Symbol *GetBaseSymbol() const;
llvm::Constant *GetConstant(const Type *type) const;
const Type *type;

View File

@@ -63,7 +63,7 @@
#include <llvm/Support/StandardPasses.h>
#else
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#endif // LLVM_2_8
#endif // LLVM_2_9
#include <llvm/ADT/Triple.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/IPO.h>

View File

@@ -107,7 +107,7 @@ def run_tasks_from_queue(queue):
# is this a test to make sure an error is issued?
want_error = (filename.find("tests_errors") != -1)
if want_error == True:
ispc_cmd = "ispc --nowrap --woff %s --arch=%s --target=%s" % \
ispc_cmd = "ispc --nowrap %s --arch=%s --target=%s" % \
( filename, options.arch, options.target)
sp = subprocess.Popen(shlex.split(ispc_cmd), stdin=None, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)

12
tests/ptr-cmp-1.ispc Normal file
View File

@@ -0,0 +1,12 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * varying b = &aFOO[10];
uniform float * uniform c = aFOO;
RET[programIndex] = (b > c) ? 10 : 0;
}
export void result(uniform float RET[]) {
RET[programIndex] = 10;
}

12
tests/ptr-cmp-2.ispc Normal file
View File

@@ -0,0 +1,12 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * uniform b = aFOO;
uniform float * uniform c = aFOO;
RET[programIndex] = (c <= b) ? 10 : 0;
}
export void result(uniform float RET[]) {
RET[programIndex] = 10;
}

12
tests/ptr-diff-4.ispc Normal file
View File

@@ -0,0 +1,12 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * varying b = aFOO;
b = 5 + b;
RET[programIndex] = b - aFOO;
}
export void result(uniform float RET[]) {
RET[programIndex] = 5;
}

12
tests/ptr-diff-5.ispc Normal file
View File

@@ -0,0 +1,12 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * varying b = aFOO;
uniform float * uniform c = &aFOO[10];
RET[programIndex] = c - b;
}
export void result(uniform float RET[]) {
RET[programIndex] = 10;
}

12
tests/ptr-diff-6.ispc Normal file
View File

@@ -0,0 +1,12 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * varying b = &aFOO[10];
uniform float * uniform c = aFOO;
RET[programIndex] = b - c;
}
export void result(uniform float RET[]) {
RET[programIndex] = 10;
}

14
tests/ptr-int-1.ispc Normal file
View File

@@ -0,0 +1,14 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform int a = 1;
uniform int * uniform b = &a;
int64 pi = (int64)b;
RET[programIndex] = *((uniform int * varying)pi);
}
export void result(uniform float RET[]) {
RET[programIndex] = 1;
}

View File

@@ -0,0 +1,4 @@
// Function can't have both "task" and "export" qualifiers
export task void foo() {
}

View File

@@ -0,0 +1,6 @@
// Pointer type cast of type "int32 * uniform" to integer type "uniform int32" may lose information.
int32 foo(int * uniform x) {
return (int32) x;
}

View File

@@ -0,0 +1,6 @@
// Can't type cast from varying type "int32" to uniform type "uniform int32"
uniform int foo(int x) {
return (uniform int) x;
}