diff options
author | Zack Rusin <[email protected]> | 2013-12-18 19:16:07 -0500 |
---|---|---|
committer | Zack Rusin <[email protected]> | 2014-01-16 16:33:57 -0500 |
commit | 93b953d139112bea1c9c64a3de462cbb52c544fd (patch) | |
tree | ffb9b2c3867a9b396c361645aab66f3786cb5d37 /src/gallium/auxiliary/gallivm | |
parent | dd687fb8d090f08d09ac5e350a92f38ded837788 (diff) |
llvmpipe: do constant buffer bounds checking in shaders
It's possible to bind a smaller buffer as a constant buffer, than
what the shader actually uses/requires. This could cause nasty
crashes. This patch adds the architecture to pass the maximum
allowable constant buffer index to the jit to let it make
sure that the constant buffer indices are always within bounds.
The behavior follows the d3d10 spec, which says the overflow
should always return all zeros, and overflow is only defined
as access beyond the size of the currently bound buffer. Accesses
beyond the declared shader constant register size are not
considered an overflow and expected to return garbage but consistent
garbage (we follow the behavior which some wlk tests expect which
is to return the actual values from the bound buffer).
Signed-off-by: Zack Rusin <[email protected]>
Reviewed-by: Jose Fonseca <[email protected]>
Reviewed-by: Brian Paul <[email protected]>
Reviewed-by: Roland Scheidegger <[email protected]>
Diffstat (limited to 'src/gallium/auxiliary/gallivm')
-rw-r--r-- | src/gallium/auxiliary/gallivm/lp_bld_tgsi.h | 2 | ||||
-rw-r--r-- | src/gallium/auxiliary/gallivm/lp_bld_tgsi_soa.c | 111 |
2 files changed, 98 insertions, 15 deletions
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_tgsi.h b/src/gallium/auxiliary/gallivm/lp_bld_tgsi.h index 881cd5ba829..4f988b8fa4d 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_tgsi.h +++ b/src/gallium/auxiliary/gallivm/lp_bld_tgsi.h @@ -225,6 +225,7 @@ lp_build_tgsi_soa(struct gallivm_state *gallivm, struct lp_type type, struct lp_build_mask_context *mask, LLVMValueRef consts_ptr, + LLVMValueRef const_sizes_ptr, const struct lp_bld_tgsi_system_values *system_values, const LLVMValueRef (*inputs)[4], LLVMValueRef (*outputs)[4], @@ -433,6 +434,7 @@ struct lp_build_tgsi_soa_context LLVMValueRef max_output_vertices_vec; LLVMValueRef consts_ptr; + LLVMValueRef const_sizes_ptr; const LLVMValueRef (*inputs)[TGSI_NUM_CHANNELS]; LLVMValueRef (*outputs)[TGSI_NUM_CHANNELS]; diff --git a/src/gallium/auxiliary/gallivm/lp_bld_tgsi_soa.c b/src/gallium/auxiliary/gallivm/lp_bld_tgsi_soa.c index 6d8dc8c6f19..d9b21f831ca 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_tgsi_soa.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_tgsi_soa.c @@ -789,11 +789,19 @@ gather_outputs(struct lp_build_tgsi_soa_context * bld) static LLVMValueRef build_gather(struct lp_build_context *bld, LLVMValueRef base_ptr, - LLVMValueRef indexes) + LLVMValueRef indexes, + LLVMValueRef *overflow_mask) { LLVMBuilderRef builder = bld->gallivm->builder; LLVMValueRef res = bld->undef; unsigned i; + LLVMValueRef temp_ptr; + + if (overflow_mask) { + temp_ptr = lp_build_alloca( + bld->gallivm, + lp_build_vec_type(bld->gallivm, bld->type), ""); + } /* * Loop over elements of index_vec, load scalar value, insert it into 'res'. @@ -802,11 +810,54 @@ build_gather(struct lp_build_context *bld, LLVMValueRef ii = lp_build_const_int32(bld->gallivm, i); LLVMValueRef index = LLVMBuildExtractElement(builder, indexes, ii, ""); - LLVMValueRef scalar_ptr = LLVMBuildGEP(builder, base_ptr, - &index, 1, "gather_ptr"); - LLVMValueRef scalar = LLVMBuildLoad(builder, scalar_ptr, ""); + LLVMValueRef scalar_ptr, scalar; + LLVMValueRef overflow; + struct lp_build_if_state if_ctx; + + /* + * overflow_mask is a boolean vector telling us which channels + * in the vector overflowed. We use the overflow behavior for + * constant buffers which is defined as: + * Out of bounds access to constant buffer returns 0 in all + * componenets. Out of bounds behavior is always with respect + * to the size of the buffer bound at that slot. + */ + if (overflow_mask) { + overflow = LLVMBuildExtractElement(builder, *overflow_mask, + ii, ""); + lp_build_if(&if_ctx, bld->gallivm, overflow); + { + LLVMValueRef val = LLVMBuildLoad(builder, temp_ptr, ""); + val = LLVMBuildInsertElement( + builder, val, + LLVMConstNull(LLVMFloatTypeInContext(bld->gallivm->context)), + ii, ""); + LLVMBuildStore(builder, val, temp_ptr); + } + lp_build_else(&if_ctx); + { + LLVMValueRef val = LLVMBuildLoad(builder, temp_ptr, ""); + + scalar_ptr = LLVMBuildGEP(builder, base_ptr, + &index, 1, "gather_ptr"); + scalar = LLVMBuildLoad(builder, scalar_ptr, ""); + + val = LLVMBuildInsertElement(builder, val, scalar, ii, ""); - res = LLVMBuildInsertElement(builder, res, scalar, ii, ""); + LLVMBuildStore(builder, val, temp_ptr); + } + lp_build_endif(&if_ctx); + } else { + scalar_ptr = LLVMBuildGEP(builder, base_ptr, + &index, 1, "gather_ptr"); + scalar = LLVMBuildLoad(builder, scalar_ptr, ""); + + res = LLVMBuildInsertElement(builder, res, scalar, ii, ""); + } + } + + if (overflow_mask) { + res = LLVMBuildLoad(builder, temp_ptr, "gather_val"); } return res; @@ -912,12 +963,23 @@ get_indirect_index(struct lp_build_tgsi_soa_context *bld, index = lp_build_add(uint_bld, base, rel); - max_index = lp_build_const_int_vec(bld->bld_base.base.gallivm, - uint_bld->type, - bld->bld_base.info->file_max[reg_file]); + /* + * emit_fetch_constant handles constant buffer overflow so this code + * is pointless for them. + * Furthermore the D3D10 spec in section 6.5 says: + * If the constant buffer bound to a slot is larger than the size + * declared in the shader for that slot, implementations are allowed + * to return incorrect data (not necessarily 0) for indices that are + * larger than the declared size but smaller than the buffer size. + */ + if (reg_file != TGSI_FILE_CONSTANT) { + max_index = lp_build_const_int_vec(bld->bld_base.base.gallivm, + uint_bld->type, + bld->bld_base.info->file_max[reg_file]); - assert(!uint_bld->type.sign); - index = lp_build_min(uint_bld, index, max_index); + assert(!uint_bld->type.sign); + index = lp_build_min(uint_bld, index, max_index); + } return index; } @@ -996,6 +1058,7 @@ emit_fetch_constant( unsigned dimension = 0; LLVMValueRef dimension_index; LLVMValueRef consts_ptr; + LLVMValueRef num_consts; LLVMValueRef res; /* XXX: Handle fetching xyzw components as a vector */ @@ -1008,25 +1071,40 @@ emit_fetch_constant( } dimension_index = lp_build_const_int32(gallivm, dimension); - consts_ptr = lp_build_array_get(gallivm, bld->consts_ptr, dimension_index); + consts_ptr = + lp_build_array_get(gallivm, bld->consts_ptr, dimension_index); + num_consts = + lp_build_array_get(gallivm, bld->const_sizes_ptr, dimension_index); if (reg->Register.Indirect) { LLVMValueRef indirect_index; LLVMValueRef swizzle_vec = lp_build_const_int_vec(gallivm, uint_bld->type, swizzle); LLVMValueRef index_vec; /* index into the const buffer */ + LLVMValueRef overflow_mask; indirect_index = get_indirect_index(bld, reg->Register.File, reg->Register.Index, ®->Indirect); + /* All fetches are from the same constant buffer, so + * we need to propagate the size to a vector to do a + * vector comparison */ + num_consts = lp_build_broadcast_scalar(uint_bld, num_consts); + /* Construct a boolean vector telling us which channels + * overflow the bound constant buffer */ + overflow_mask = LLVMBuildICmp(builder, LLVMIntUGE, + indirect_index, + num_consts, ""); + /* index_vec = indirect_index * 4 + swizzle */ index_vec = lp_build_shl_imm(uint_bld, indirect_index, 2); index_vec = lp_build_add(uint_bld, index_vec, swizzle_vec); /* Gather values from the constant buffer */ - res = build_gather(&bld_base->base, consts_ptr, index_vec); + res = build_gather(&bld_base->base, consts_ptr, index_vec, + &overflow_mask); } else { LLVMValueRef index; /* index into the const buffer */ @@ -1044,6 +1122,7 @@ emit_fetch_constant( struct lp_build_context *bld_fetch = stype_to_fetch(bld_base, stype); res = LLVMBuildBitCast(builder, res, bld_fetch->vec_type, ""); } + return res; } @@ -1085,7 +1164,7 @@ emit_fetch_immediate( imms_array = LLVMBuildBitCast(builder, bld->imms_array, fptr_type, ""); /* Gather values from the immediate register array */ - res = build_gather(&bld_base->base, imms_array, index_vec); + res = build_gather(&bld_base->base, imms_array, index_vec, NULL); } else { res = bld->immediates[reg->Register.Index][swizzle]; @@ -1132,7 +1211,7 @@ emit_fetch_input( inputs_array = LLVMBuildBitCast(builder, bld->inputs_array, fptr_type, ""); /* Gather values from the input register array */ - res = build_gather(&bld_base->base, inputs_array, index_vec); + res = build_gather(&bld_base->base, inputs_array, index_vec, NULL); } else { if (bld->indirect_files & (1 << TGSI_FILE_INPUT)) { LLVMValueRef lindex = lp_build_const_int32(gallivm, @@ -1242,7 +1321,7 @@ emit_fetch_temporary( temps_array = LLVMBuildBitCast(builder, bld->temps_array, fptr_type, ""); /* Gather values from the temporary register array */ - res = build_gather(&bld_base->base, temps_array, index_vec); + res = build_gather(&bld_base->base, temps_array, index_vec, NULL); } else { LLVMValueRef temp_ptr; @@ -3352,6 +3431,7 @@ lp_build_tgsi_soa(struct gallivm_state *gallivm, struct lp_type type, struct lp_build_mask_context *mask, LLVMValueRef consts_ptr, + LLVMValueRef const_sizes_ptr, const struct lp_bld_tgsi_system_values *system_values, const LLVMValueRef (*inputs)[TGSI_NUM_CHANNELS], LLVMValueRef (*outputs)[TGSI_NUM_CHANNELS], @@ -3379,6 +3459,7 @@ lp_build_tgsi_soa(struct gallivm_state *gallivm, bld.inputs = inputs; bld.outputs = outputs; bld.consts_ptr = consts_ptr; + bld.const_sizes_ptr = const_sizes_ptr; bld.sampler = sampler; bld.bld_base.info = info; bld.indirect_files = info->indirect_files; |