diff --git a/builtins.m4 b/builtins.m4 index 7aaa3546..957af297 100644 --- a/builtins.m4 +++ b/builtins.m4 @@ -1777,6 +1777,43 @@ prefetch_read(varying_int64, <$1 x i64>) prefetch_read(varying_float, <$1 x float>) prefetch_read(varying_double, <$1 x double>) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; assert + +declare i32 @printf(i8*, ...) +declare void @abort() noreturn + +define internal void @__do_assert_uniform(i8 *%str, i1 %test, <$1 x i32> %mask) { + br i1 %test, label %ok, label %fail + +fail: + %call = call i32 (i8*, ...)* @printf(i8* %str) + call void @abort() noreturn + unreachable + +ok: + ret void +} + + +define internal void @__do_assert_varying(i8 *%str, <$1 x i32> %test, + <$1 x i32> %mask) { + %nottest = xor <$1 x i32> %test, + < forloop(i, 1, eval($1-1), `i32 -1, ') i32 -1 > + %nottest_and_mask = and <$1 x i32> %nottest, %mask + %mm = call i32 @__movmsk(<$1 x i32> %nottest_and_mask) + %all_ok = icmp eq i32 %mm, 0 + br i1 %all_ok, label %ok, label %fail + +fail: + %call = call i32 (i8*, ...)* @printf(i8* %str) + call void @abort() noreturn + unreachable + +ok: + ret void +} + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; stdlib transcendentals ;; diff --git a/docs/ispc.txt b/docs/ispc.txt index 53c2bfa2..f0659b48 100644 --- a/docs/ispc.txt +++ b/docs/ispc.txt @@ -89,6 +89,7 @@ Contents: + `Math Functions`_ + `Output Functions`_ + + `Assertions`_ + `Cross-Program Instance Operations`_ + `Converting Between Array-of-Structures and Structure-of-Arrays Layout`_ + `Packed Load and Store Operations`_ @@ -1840,6 +1841,35 @@ values for the inactive program instances aren't printed. (In other cases, they may have garbage values or be otherwise undefined.) +Assertions +---------- + +The ``ispc`` standard library includes a mechanism for adding ``assert()`` +statements to ``ispc`` program code. Like ``assert()`` in C, the +``assert()`` function takes a single boolean expression as an argument. If +the expression evaluates to true at runtime, then a diagnostic error +message printed and the ``abort()`` function is called. + +When called with a ``varying`` quantity, an assertion triggers if the +expression evaluates to true for any any of the executing program instances +at the point where it is called. Thus, given code like: + +:: + + int x = programIndex - 2; // (-2, -1, 0, ... ) + if (x > 0) + assert(x > 0); + +The ``assert()`` statement will not trigger, since the condition isn't true +for any of the executing program instances at that point. (If this +``assert()`` statement was outside of this ``if``, then it would of course +trigger.) + +To disable all of the assertions in a file that is being compiled (e.g., +for an optimized release build), use the ``--opt=disable-assertions`` +command-line argument. + + Cross-Program Instance Operations --------------------------------- diff --git a/ispc.cpp b/ispc.cpp index ae6585c5..3a3d3751 100644 --- a/ispc.cpp +++ b/ispc.cpp @@ -283,6 +283,7 @@ Opt::Opt() { fastMath = false; fastMaskedVload = false; unrollLoops = true; + disableAsserts = false; disableBlendedMaskedStores = false; disableCoherentControlFlow = false; disableUniformControlFlow = false; diff --git a/ispc.h b/ispc.h index 2f952a54..ba5ce632 100644 --- a/ispc.h +++ b/ispc.h @@ -226,6 +226,10 @@ struct Opt { it will make sense. */ bool unrollLoops; + /** Indicates whether assert() statements should be ignored (for + performance in the generated code). */ + bool disableAsserts; + /** On targets that don't have a masked store instruction but do have a blending instruction, by default, we simulate masked stores by loading the old value, blending, and storing the result. This can @@ -365,6 +369,7 @@ enum { COST_TYPECAST_SIMPLE = 1, COST_UNIFORM_LOOP = 4, COST_VARYING_LOOP = 6, + COST_ASSERT = 8, CHECK_MASK_AT_FUNCTION_START_COST = 16, PREDICATE_SAFE_IF_STATEMENT_COST = 6, diff --git a/lex.ll b/lex.ll index 3e622311..e752dc8d 100644 --- a/lex.ll +++ b/lex.ll @@ -78,6 +78,7 @@ ZO_SWIZZLE ([01]+[w-z]+)+|([01]+[rgba]+)+|([01]+[uv]+)+ "/*" { lCComment(yylloc); } "//" { lCppComment(yylloc); } +__assert { return TOKEN_ASSERT; } bool { return TOKEN_BOOL; } break { return TOKEN_BREAK; } case { return TOKEN_CASE; } diff --git a/main.cpp b/main.cpp index 5efb2118..72ec1355 100644 --- a/main.cpp +++ b/main.cpp @@ -83,6 +83,7 @@ static void usage(int ret) { printf(" [-o /--outfile=]\tOutput filename (may be \"-\" for standard output)\n"); printf(" [-O0/-O1]\t\t\t\tSet optimization level (-O1 is default)\n"); printf(" [--opt=