LLVMValueRef gen_xor(compile_t* c, ast_t* left, ast_t* right) { LLVMValueRef l_value = gen_expr(c, left); LLVMValueRef r_value = gen_expr(c, right); if((l_value == NULL) || (r_value == NULL)) return NULL; if(LLVMIsConstant(l_value) && LLVMIsConstant(r_value)) return LLVMConstXor(l_value, r_value); if(is_always_true(c, l_value)) return LLVMBuildNot(c->builder, r_value, ""); if(is_always_false(c, l_value)) return r_value; if(is_always_true(c, r_value)) return LLVMBuildNot(c->builder, l_value, ""); if(is_always_false(c, r_value)) return l_value; return LLVMBuildXor(c->builder, l_value, r_value, ""); }
/** * Return (a & ~b) */ LLVMValueRef lp_build_andnot(struct lp_build_context *bld, LLVMValueRef a, LLVMValueRef b) { LLVMBuilderRef builder = bld->gallivm->builder; const struct lp_type type = bld->type; LLVMValueRef res; assert(lp_check_value(type, a)); assert(lp_check_value(type, b)); /* can't do bitwise ops on floating-point values */ if (type.floating) { a = LLVMBuildBitCast(builder, a, bld->int_vec_type, ""); b = LLVMBuildBitCast(builder, b, bld->int_vec_type, ""); } res = LLVMBuildNot(builder, b, ""); res = LLVMBuildAnd(builder, a, res, ""); if (type.floating) { res = LLVMBuildBitCast(builder, res, bld->vec_type, ""); } return res; }
LLVMValueRef gen_not(compile_t* c, ast_t* ast) { LLVMValueRef value = gen_expr(c, ast); if(value == NULL) return NULL; ast_t* type = ast_type(ast); if(is_bool(type)) { if(LLVMIsAConstantInt(value)) { if(is_always_true(value)) return LLVMConstInt(c->ibool, 0, false); return LLVMConstInt(c->ibool, 1, false); } LLVMValueRef test = LLVMBuildICmp(c->builder, LLVMIntEQ, value, LLVMConstInt(c->ibool, 0, false), ""); return LLVMBuildZExt(c->builder, test, c->ibool, ""); } if(LLVMIsAConstantInt(value)) return LLVMConstNot(value); return LLVMBuildNot(c->builder, value, ""); }
/** * Build a manual selection sequence for cube face sc/tc coordinates and * major axis vector (multiplied by 2 for consistency) for the given * vec3 \p coords, for the face implied by \p selcoords. * * For the major axis, we always adjust the sign to be in the direction of * selcoords.ma; i.e., a positive out_ma means that coords is pointed towards * the selcoords major axis. */ static void build_cube_select(LLVMBuilderRef builder, const struct cube_selection_coords *selcoords, const LLVMValueRef *coords, LLVMValueRef *out_st, LLVMValueRef *out_ma) { LLVMTypeRef f32 = LLVMTypeOf(coords[0]); LLVMValueRef is_ma_positive; LLVMValueRef sgn_ma; LLVMValueRef is_ma_z, is_not_ma_z; LLVMValueRef is_ma_y; LLVMValueRef is_ma_x; LLVMValueRef sgn; LLVMValueRef tmp; is_ma_positive = LLVMBuildFCmp(builder, LLVMRealUGE, selcoords->ma, LLVMConstReal(f32, 0.0), ""); sgn_ma = LLVMBuildSelect(builder, is_ma_positive, LLVMConstReal(f32, 1.0), LLVMConstReal(f32, -1.0), ""); is_ma_z = LLVMBuildFCmp(builder, LLVMRealUGE, selcoords->id, LLVMConstReal(f32, 4.0), ""); is_not_ma_z = LLVMBuildNot(builder, is_ma_z, ""); is_ma_y = LLVMBuildAnd(builder, is_not_ma_z, LLVMBuildFCmp(builder, LLVMRealUGE, selcoords->id, LLVMConstReal(f32, 2.0), ""), ""); is_ma_x = LLVMBuildAnd(builder, is_not_ma_z, LLVMBuildNot(builder, is_ma_y, ""), ""); /* Select sc */ tmp = LLVMBuildSelect(builder, is_ma_z, coords[2], coords[0], ""); sgn = LLVMBuildSelect(builder, is_ma_y, LLVMConstReal(f32, 1.0), LLVMBuildSelect(builder, is_ma_x, sgn_ma, LLVMBuildFNeg(builder, sgn_ma, ""), ""), ""); out_st[0] = LLVMBuildFMul(builder, tmp, sgn, ""); /* Select tc */ tmp = LLVMBuildSelect(builder, is_ma_y, coords[2], coords[1], ""); sgn = LLVMBuildSelect(builder, is_ma_y, LLVMBuildFNeg(builder, sgn_ma, ""), LLVMConstReal(f32, -1.0), ""); out_st[1] = LLVMBuildFMul(builder, tmp, sgn, ""); /* Select ma */ tmp = LLVMBuildSelect(builder, is_ma_z, coords[2], LLVMBuildSelect(builder, is_ma_y, coords[1], coords[0], ""), ""); sgn = LLVMBuildSelect(builder, is_ma_positive, LLVMConstReal(f32, 2.0), LLVMConstReal(f32, -2.0), ""); *out_ma = LLVMBuildFMul(builder, tmp, sgn, ""); }
static void emit_not(const struct lp_build_tgsi_action *action, struct lp_build_tgsi_context *bld_base, struct lp_build_emit_data *emit_data) { LLVMBuilderRef builder = bld_base->base.gallivm->builder; LLVMValueRef v = bitcast(bld_base, TGSI_TYPE_UNSIGNED, emit_data->args[0]); emit_data->output[emit_data->chan] = LLVMBuildNot(builder, v, ""); }
LLVMValueRef gen_not(compile_t* c, ast_t* ast) { LLVMValueRef value = gen_expr(c, ast); if(value == NULL) return NULL; if(LLVMIsAConstantInt(value)) return LLVMConstNot(value); return LLVMBuildNot(c->builder, value, ""); }
static void ac_analyze_position_w(struct ac_llvm_context *ctx, LLVMValueRef pos[3][4], struct ac_position_w_info *w) { LLVMBuilderRef builder = ctx->builder; LLVMValueRef all_w_negative = ctx->i1true; w->w_reflection = ctx->i1false; w->any_w_negative = ctx->i1false; for (unsigned i = 0; i < 3; i++) { LLVMValueRef neg_w; neg_w = LLVMBuildFCmp(builder, LLVMRealOLT, pos[i][3], ctx->f32_0, ""); /* If neg_w is true, negate w_reflection. */ w->w_reflection = LLVMBuildXor(builder, w->w_reflection, neg_w, ""); w->any_w_negative = LLVMBuildOr(builder, w->any_w_negative, neg_w, ""); all_w_negative = LLVMBuildAnd(builder, all_w_negative, neg_w, ""); } w->all_w_positive = LLVMBuildNot(builder, w->any_w_negative, ""); w->w_accepted = LLVMBuildNot(builder, all_w_negative, ""); }
LLVMValueRef gen_isnt(compile_t* c, ast_t* ast) { AST_GET_CHILDREN(ast, left, right); ast_t* left_type = ast_type(left); ast_t* right_type = ast_type(right); LLVMValueRef l_value = gen_expr(c, left); LLVMValueRef r_value = gen_expr(c, right); if((l_value == NULL) || (r_value == NULL)) return NULL; codegen_debugloc(c, ast); LLVMValueRef result = gen_is_value(c, left_type, right_type, l_value, r_value); result = LLVMBuildNot(c->builder, result, ""); LLVMValueRef value = LLVMBuildZExt(c->builder, result, c->ibool, ""); codegen_debugloc(c, NULL); return value; }
/** * Return (mask & a) | (~mask & b); */ LLVMValueRef lp_build_select_bitwise(struct lp_build_context *bld, LLVMValueRef mask, LLVMValueRef a, LLVMValueRef b) { LLVMBuilderRef builder = bld->gallivm->builder; struct lp_type type = bld->type; LLVMValueRef res; assert(lp_check_value(type, a)); assert(lp_check_value(type, b)); if (a == b) { return a; } if(type.floating) { LLVMTypeRef int_vec_type = lp_build_int_vec_type(bld->gallivm, type); a = LLVMBuildBitCast(builder, a, int_vec_type, ""); b = LLVMBuildBitCast(builder, b, int_vec_type, ""); } a = LLVMBuildAnd(builder, a, mask, ""); /* This often gets translated to PANDN, but sometimes the NOT is * pre-computed and stored in another constant. The best strategy depends * on available registers, so it is not a big deal -- hopefully LLVM does * the right decision attending the rest of the program. */ b = LLVMBuildAnd(builder, b, LLVMBuildNot(builder, mask, ""), ""); res = LLVMBuildOr(builder, a, b, ""); if(type.floating) { LLVMTypeRef vec_type = lp_build_vec_type(bld->gallivm, type); res = LLVMBuildBitCast(builder, res, vec_type, ""); } return res; }
/** * Generate 1 - a, or ~a depending on bld->type. */ LLVMValueRef lp_build_comp(struct lp_build_context *bld, LLVMValueRef a) { const struct lp_type type = bld->type; if(a == bld->one) return bld->zero; if(a == bld->zero) return bld->one; if(type.norm && !type.floating && !type.fixed && !type.sign) { if(LLVMIsConstant(a)) return LLVMConstNot(a); else return LLVMBuildNot(bld->builder, a, ""); } if(LLVMIsConstant(a)) return LLVMConstSub(bld->one, a); else return LLVMBuildSub(bld->builder, bld->one, a, ""); }
/** * Register store. */ void lp_emit_store_aos( struct lp_build_tgsi_aos_context *bld, const struct tgsi_full_instruction *inst, unsigned index, LLVMValueRef value) { LLVMBuilderRef builder = bld->bld_base.base.gallivm->builder; const struct tgsi_full_dst_register *reg = &inst->Dst[index]; LLVMValueRef mask = NULL; LLVMValueRef ptr; /* * Saturate the value */ switch (inst->Instruction.Saturate) { case TGSI_SAT_NONE: break; case TGSI_SAT_ZERO_ONE: value = lp_build_max(&bld->bld_base.base, value, bld->bld_base.base.zero); value = lp_build_min(&bld->bld_base.base, value, bld->bld_base.base.one); break; case TGSI_SAT_MINUS_PLUS_ONE: value = lp_build_max(&bld->bld_base.base, value, lp_build_const_vec(bld->bld_base.base.gallivm, bld->bld_base.base.type, -1.0)); value = lp_build_min(&bld->bld_base.base, value, bld->bld_base.base.one); break; default: assert(0); } /* * Translate the register file */ assert(!reg->Register.Indirect); switch (reg->Register.File) { case TGSI_FILE_OUTPUT: ptr = bld->outputs[reg->Register.Index]; break; case TGSI_FILE_TEMPORARY: ptr = bld->temps[reg->Register.Index]; break; case TGSI_FILE_ADDRESS: ptr = bld->addr[reg->Indirect.Index]; break; case TGSI_FILE_PREDICATE: ptr = bld->preds[reg->Register.Index]; break; default: assert(0); return; } if (!ptr) return; /* * Predicate */ if (inst->Instruction.Predicate) { LLVMValueRef pred; assert(inst->Predicate.Index < LP_MAX_TGSI_PREDS); pred = LLVMBuildLoad(builder, bld->preds[inst->Predicate.Index], ""); /* * Convert the value to an integer mask. */ pred = lp_build_compare(bld->bld_base.base.gallivm, bld->bld_base.base.type, PIPE_FUNC_NOTEQUAL, pred, bld->bld_base.base.zero); if (inst->Predicate.Negate) { pred = LLVMBuildNot(builder, pred, ""); } pred = bld->bld_base.emit_swizzle(&bld->bld_base, pred, inst->Predicate.SwizzleX, inst->Predicate.SwizzleY, inst->Predicate.SwizzleZ, inst->Predicate.SwizzleW); if (mask) { mask = LLVMBuildAnd(builder, mask, pred, ""); } else { mask = pred; } } /* * Writemask */ if (reg->Register.WriteMask != TGSI_WRITEMASK_XYZW) { LLVMValueRef writemask; writemask = lp_build_const_mask_aos_swizzled(bld->bld_base.base.gallivm, bld->bld_base.base.type, reg->Register.WriteMask, TGSI_NUM_CHANNELS, bld->swizzles); if (mask) { mask = LLVMBuildAnd(builder, mask, writemask, ""); } else { mask = writemask; } } if (mask) { LLVMValueRef orig_value; orig_value = LLVMBuildLoad(builder, ptr, ""); value = lp_build_select(&bld->bld_base.base, mask, value, orig_value); } LLVMBuildStore(builder, value, ptr); }
/** * Apply the stencil operator (add/sub/keep/etc) to the given vector * of stencil values. * \return new stencil values vector */ static LLVMValueRef lp_build_stencil_op_single(struct lp_build_context *bld, const struct pipe_stencil_state *stencil, enum stencil_op op, LLVMValueRef stencilRef, LLVMValueRef stencilVals) { LLVMBuilderRef builder = bld->gallivm->builder; struct lp_type type = bld->type; LLVMValueRef res; LLVMValueRef max = lp_build_const_int_vec(bld->gallivm, type, 0xff); unsigned stencil_op; assert(type.sign); switch (op) { case S_FAIL_OP: stencil_op = stencil->fail_op; break; case Z_FAIL_OP: stencil_op = stencil->zfail_op; break; case Z_PASS_OP: stencil_op = stencil->zpass_op; break; default: assert(0 && "Invalid stencil_op mode"); stencil_op = PIPE_STENCIL_OP_KEEP; } switch (stencil_op) { case PIPE_STENCIL_OP_KEEP: res = stencilVals; /* we can return early for this case */ return res; case PIPE_STENCIL_OP_ZERO: res = bld->zero; break; case PIPE_STENCIL_OP_REPLACE: res = stencilRef; break; case PIPE_STENCIL_OP_INCR: res = lp_build_add(bld, stencilVals, bld->one); res = lp_build_min(bld, res, max); break; case PIPE_STENCIL_OP_DECR: res = lp_build_sub(bld, stencilVals, bld->one); res = lp_build_max(bld, res, bld->zero); break; case PIPE_STENCIL_OP_INCR_WRAP: res = lp_build_add(bld, stencilVals, bld->one); res = LLVMBuildAnd(builder, res, max, ""); break; case PIPE_STENCIL_OP_DECR_WRAP: res = lp_build_sub(bld, stencilVals, bld->one); res = LLVMBuildAnd(builder, res, max, ""); break; case PIPE_STENCIL_OP_INVERT: res = LLVMBuildNot(builder, stencilVals, ""); res = LLVMBuildAnd(builder, res, max, ""); break; default: assert(0 && "bad stencil op mode"); res = bld->undef; } return res; }
LLVMValueRef lp_build_logicop(LLVMBuilderRef builder, unsigned logicop_func, LLVMValueRef src, LLVMValueRef dst) { LLVMTypeRef type; LLVMValueRef res; type = LLVMTypeOf(src); switch (logicop_func) { case PIPE_LOGICOP_CLEAR: res = LLVMConstNull(type); break; case PIPE_LOGICOP_NOR: res = LLVMBuildNot(builder, LLVMBuildOr(builder, src, dst, ""), ""); break; case PIPE_LOGICOP_AND_INVERTED: res = LLVMBuildAnd(builder, LLVMBuildNot(builder, src, ""), dst, ""); break; case PIPE_LOGICOP_COPY_INVERTED: res = LLVMBuildNot(builder, src, ""); break; case PIPE_LOGICOP_AND_REVERSE: res = LLVMBuildAnd(builder, src, LLVMBuildNot(builder, dst, ""), ""); break; case PIPE_LOGICOP_INVERT: res = LLVMBuildNot(builder, dst, ""); break; case PIPE_LOGICOP_XOR: res = LLVMBuildXor(builder, src, dst, ""); break; case PIPE_LOGICOP_NAND: res = LLVMBuildNot(builder, LLVMBuildAnd(builder, src, dst, ""), ""); break; case PIPE_LOGICOP_AND: res = LLVMBuildAnd(builder, src, dst, ""); break; case PIPE_LOGICOP_EQUIV: res = LLVMBuildNot(builder, LLVMBuildXor(builder, src, dst, ""), ""); break; case PIPE_LOGICOP_NOOP: res = dst; break; case PIPE_LOGICOP_OR_INVERTED: res = LLVMBuildOr(builder, LLVMBuildNot(builder, src, ""), dst, ""); break; case PIPE_LOGICOP_COPY: res = src; break; case PIPE_LOGICOP_OR_REVERSE: res = LLVMBuildOr(builder, src, LLVMBuildNot(builder, dst, ""), ""); break; case PIPE_LOGICOP_OR: res = LLVMBuildOr(builder, src, dst, ""); break; case PIPE_LOGICOP_SET: res = LLVMConstAllOnes(type); break; default: assert(0); res = src; } return res; }
/** * Apply the stencil operator (add/sub/keep/etc) to the given vector * of stencil values. * \return new stencil values vector */ static LLVMValueRef lp_build_stencil_op_single(struct lp_build_context *bld, const struct pipe_stencil_state *stencil, enum stencil_op op, LLVMValueRef stencilRef, LLVMValueRef stencilVals, LLVMValueRef mask) { const unsigned stencilMax = 255; /* XXX fix */ struct lp_type type = bld->type; LLVMValueRef res; LLVMValueRef max = lp_build_const_int_vec(type, stencilMax); unsigned stencil_op; assert(type.sign); switch (op) { case S_FAIL_OP: stencil_op = stencil->fail_op; break; case Z_FAIL_OP: stencil_op = stencil->zfail_op; break; case Z_PASS_OP: stencil_op = stencil->zpass_op; break; default: assert(0 && "Invalid stencil_op mode"); stencil_op = PIPE_STENCIL_OP_KEEP; } switch (stencil_op) { case PIPE_STENCIL_OP_KEEP: res = stencilVals; /* we can return early for this case */ return res; case PIPE_STENCIL_OP_ZERO: res = bld->zero; break; case PIPE_STENCIL_OP_REPLACE: res = stencilRef; break; case PIPE_STENCIL_OP_INCR: res = lp_build_add(bld, stencilVals, bld->one); res = lp_build_min(bld, res, max); break; case PIPE_STENCIL_OP_DECR: res = lp_build_sub(bld, stencilVals, bld->one); res = lp_build_max(bld, res, bld->zero); break; case PIPE_STENCIL_OP_INCR_WRAP: res = lp_build_add(bld, stencilVals, bld->one); res = LLVMBuildAnd(bld->builder, res, max, ""); break; case PIPE_STENCIL_OP_DECR_WRAP: res = lp_build_sub(bld, stencilVals, bld->one); res = LLVMBuildAnd(bld->builder, res, max, ""); break; case PIPE_STENCIL_OP_INVERT: res = LLVMBuildNot(bld->builder, stencilVals, ""); res = LLVMBuildAnd(bld->builder, res, max, ""); break; default: assert(0 && "bad stencil op mode"); res = NULL; } if (stencil->writemask != stencilMax) { /* mask &= stencil->writemask */ LLVMValueRef writemask = lp_build_const_int_vec(type, stencil->writemask); mask = LLVMBuildAnd(bld->builder, mask, writemask, ""); /* res = (res & mask) | (stencilVals & ~mask) */ res = lp_build_select_bitwise(bld, writemask, res, stencilVals); } else { /* res = mask ? res : stencilVals */ res = lp_build_select(bld, mask, res, stencilVals); } return res; }
/* * llvmgen_assignment * * Generates a store operation from an assignment expression. */ LLVMValueRef llvmgen_assignment (gencodectx_t gctx, expr_node_t *lhs, expr_node_t *rhs) { LLVMBuilderRef builder = (gctx->curfn == 0 ? 0 : gctx->curfn->builder); LLVMValueRef rhsvalue, v, lhsaddr; LLVMTypeRef lhstype, rhstype; llvm_accinfo_t accinfo; int shifts_required = 0; rhsvalue = llvmgen_expression(gctx, rhs, 0); if (rhsvalue == 0) { unsigned int bpval = machine_scalar_bits(gctx->mach); expr_signal(gctx->ectx, STC__EXPRVALRQ); rhsvalue = LLVMConstNull(LLVMIntTypeInContext(gctx->llvmctx, bpval)); } rhstype = LLVMTypeOf(rhsvalue); lhsaddr = llvmgen_addr_expression(gctx, lhs, &accinfo); if (lhsaddr == 0) { expr_signal(gctx->ectx, STC__ADDRVALRQ); return rhsvalue; } // If we're assigning into a field-reference with a non-zero // bit position or a non-CTCE size, we have to do some bit-shifting // to do the store. if (accinfo.posval != 0 || accinfo.sizeval != 0) { shifts_required = 1; lhstype = LLVMIntTypeInContext(gctx->llvmctx, accinfo.width); if ((accinfo.flags & LLVMGEN_M_ACC_CONSTSIZ) != 0) { accinfo.sizeval = LLVMConstInt(gctx->fullwordtype, accinfo.size, 0); } } else if ((accinfo.flags & LLVMGEN_M_ACC_CONSTSIZ) != 0) { lhstype = LLVMIntTypeInContext(gctx->llvmctx, accinfo.size); } else { lhstype = LLVMIntTypeInContext(gctx->llvmctx, accinfo.width); } lhsaddr = llvmgen_adjustval(gctx, lhsaddr, LLVMPointerType(lhstype, 0), 0); if (shifts_required) { LLVMValueRef neg1, srcmask, dstmask, rhstemp; if (LLVMGetTypeKind(rhstype) != LLVMIntegerTypeKind) { rhsvalue = llvmgen_adjustval(gctx, rhsvalue, gctx->fullwordtype, 0); rhstype = LLVMTypeOf(rhsvalue); } else { accinfo.sizeval = llvmgen_adjustval(gctx, accinfo.sizeval, rhstype, 0); accinfo.posval = llvmgen_adjustval(gctx, accinfo.posval, rhstype, 0); } neg1 = LLVMConstAllOnes(rhstype); v = LLVMBuildShl(builder, neg1, accinfo.sizeval, llvmgen_temp(gctx)); srcmask = LLVMBuildNot(builder, v, llvmgen_temp(gctx)); v = LLVMBuildAnd(builder, rhsvalue, srcmask, llvmgen_temp(gctx)); v = LLVMBuildShl(builder, v, accinfo.posval, llvmgen_temp(gctx)); rhstemp = llvmgen_adjustval(gctx, v, lhstype, 0); v = LLVMBuildShl(builder, srcmask, accinfo.posval, llvmgen_temp(gctx)); v = llvmgen_adjustval(gctx, v, lhstype, 0); dstmask = LLVMBuildNot(builder, v, llvmgen_temp(gctx)); v = LLVMBuildLoad(builder, lhsaddr, llvmgen_temp(gctx)); v = llvmgen_adjustval(gctx, v, lhstype, (accinfo.flags & LLVMGEN_M_SEG_SIGNEXT) != 0); v = LLVMBuildAnd(builder, v, dstmask, llvmgen_temp(gctx)); v = LLVMBuildOr(builder, v, rhstemp, llvmgen_temp(gctx)); } else { v = llvmgen_adjustval(gctx, rhsvalue, lhstype, (accinfo.flags & LLVMGEN_M_SEG_SIGNEXT) != 0); } LLVMBuildStore(builder, v, lhsaddr); if ((accinfo.flags & LLVMGEN_M_SEG_VOLATILE) != 0) LLVMSetVolatile(v, 1); return rhsvalue; } /* llvmgen_assignment */
/* * gen_operator_expression * * Code generation for operator expressions. Most of them have straightforward * translations into LLVM instructions and are handled directly here. */ static LLVMValueRef gen_operator_expression (gencodectx_t gctx, expr_node_t *exp, LLVMTypeRef neededtype) { expr_node_t *lhs = expr_op_lhs(exp); expr_node_t *rhs = expr_op_rhs(exp); optype_t op = expr_op_type(exp); LLVMBuilderRef builder = gctx->curfn->builder; LLVMTypeRef inttype; LLVMValueRef lval, rval, result; if (op == OPER_FETCH) { return gen_fetch(gctx, rhs, neededtype); } if (op == OPER_ASSIGN) { LLVMValueRef val = llvmgen_assignment(gctx, lhs, rhs); return llvmgen_adjustval(gctx, val, neededtype, 0); } if (op == OPER_SHIFT) { return gen_shift(gctx, lhs, rhs, neededtype); } inttype = LLVMIntTypeInContext(gctx->llvmctx, machine_scalar_bits(gctx->mach)); lval = (lhs == 0 ? 0 : llvmgen_expression(gctx, lhs, inttype)); rval = llvmgen_expression(gctx, rhs, inttype); switch (op) { case OPER_UNARY_PLUS: result = rval; break; case OPER_UNARY_MINUS: result = LLVMBuildNeg(builder, rval, llvmgen_temp(gctx)); break; case OPER_ADD: result = LLVMBuildAdd(builder, lval, rval, llvmgen_temp(gctx)); break; case OPER_SUBTRACT: result = LLVMBuildSub(builder, lval, rval, llvmgen_temp(gctx)); break; case OPER_MULT: result = LLVMBuildMul(builder, lval, rval, llvmgen_temp(gctx)); break; case OPER_DIV: result = LLVMBuildUDiv(builder, lval, rval, llvmgen_temp(gctx)); break; case OPER_MODULO: result = LLVMBuildURem(builder, lval, rval, llvmgen_temp(gctx)); break; case OPER_AND: result = LLVMBuildAnd(builder, lval, rval, llvmgen_temp(gctx)); break; case OPER_OR: result = LLVMBuildOr(builder, lval, rval, llvmgen_temp(gctx)); break; case OPER_NOT: result = LLVMBuildNot(builder, rval, llvmgen_temp(gctx)); break; case OPER_XOR: result = LLVMBuildXor(builder, lval, rval, llvmgen_temp(gctx)); break; case OPER_EQV: result = LLVMBuildXor(builder, lval, rval, llvmgen_temp(gctx)); result = LLVMBuildNot(builder, result, llvmgen_temp(gctx)); break; default: if (op >= OPER_CMP_EQL && op <= OPER_CMP_GEQA) { result = LLVMBuildICmp(builder, llvmgen_predfromop(op, machine_addr_signed(gctx->mach)), lval, rval, llvmgen_temp(gctx)); } else { // Everything should be covered expr_signal(gctx->ectx, STC__INTCMPERR, "gen_operator_expression"); result = LLVMConstNull(inttype); } break; } return llvmgen_adjustval(gctx, result, neededtype, 0); } /* gen_operator_expression */
/** * Build code to compare two values 'a' and 'b' of 'type' using the given func. * \param func one of PIPE_FUNC_x * The result values will be 0 for false or ~0 for true. */ LLVMValueRef lp_build_compare(struct gallivm_state *gallivm, const struct lp_type type, unsigned func, LLVMValueRef a, LLVMValueRef b) { LLVMBuilderRef builder = gallivm->builder; LLVMTypeRef int_vec_type = lp_build_int_vec_type(gallivm, type); LLVMValueRef zeros = LLVMConstNull(int_vec_type); LLVMValueRef ones = LLVMConstAllOnes(int_vec_type); LLVMValueRef cond; LLVMValueRef res; assert(func >= PIPE_FUNC_NEVER); assert(func <= PIPE_FUNC_ALWAYS); assert(lp_check_value(type, a)); assert(lp_check_value(type, b)); if(func == PIPE_FUNC_NEVER) return zeros; if(func == PIPE_FUNC_ALWAYS) return ones; #if defined(PIPE_ARCH_X86) || defined(PIPE_ARCH_X86_64) /* * There are no unsigned integer comparison instructions in SSE. */ if (!type.floating && !type.sign && type.width * type.length == 128 && util_cpu_caps.has_sse2 && (func == PIPE_FUNC_LESS || func == PIPE_FUNC_LEQUAL || func == PIPE_FUNC_GREATER || func == PIPE_FUNC_GEQUAL) && (gallivm_debug & GALLIVM_DEBUG_PERF)) { debug_printf("%s: inefficient <%u x i%u> unsigned comparison\n", __FUNCTION__, type.length, type.width); } #endif #if HAVE_LLVM < 0x0207 #if defined(PIPE_ARCH_X86) || defined(PIPE_ARCH_X86_64) if(type.width * type.length == 128) { if(type.floating && util_cpu_caps.has_sse) { /* float[4] comparison */ LLVMTypeRef vec_type = lp_build_vec_type(gallivm, type); LLVMValueRef args[3]; unsigned cc; boolean swap; swap = FALSE; switch(func) { case PIPE_FUNC_EQUAL: cc = 0; break; case PIPE_FUNC_NOTEQUAL: cc = 4; break; case PIPE_FUNC_LESS: cc = 1; break; case PIPE_FUNC_LEQUAL: cc = 2; break; case PIPE_FUNC_GREATER: cc = 1; swap = TRUE; break; case PIPE_FUNC_GEQUAL: cc = 2; swap = TRUE; break; default: assert(0); return lp_build_undef(gallivm, type); } if(swap) { args[0] = b; args[1] = a; } else { args[0] = a; args[1] = b; } args[2] = LLVMConstInt(LLVMInt8TypeInContext(gallivm->context), cc, 0); res = lp_build_intrinsic(builder, "llvm.x86.sse.cmp.ps", vec_type, args, 3); res = LLVMBuildBitCast(builder, res, int_vec_type, ""); return res; } else if(util_cpu_caps.has_sse2) { /* int[4] comparison */ static const struct { unsigned swap:1; unsigned eq:1; unsigned gt:1; unsigned not:1; } table[] = { {0, 0, 0, 1}, /* PIPE_FUNC_NEVER */ {1, 0, 1, 0}, /* PIPE_FUNC_LESS */ {0, 1, 0, 0}, /* PIPE_FUNC_EQUAL */ {0, 0, 1, 1}, /* PIPE_FUNC_LEQUAL */ {0, 0, 1, 0}, /* PIPE_FUNC_GREATER */ {0, 1, 0, 1}, /* PIPE_FUNC_NOTEQUAL */ {1, 0, 1, 1}, /* PIPE_FUNC_GEQUAL */ {0, 0, 0, 0} /* PIPE_FUNC_ALWAYS */ }; const char *pcmpeq; const char *pcmpgt; LLVMValueRef args[2]; LLVMValueRef res; LLVMTypeRef vec_type = lp_build_vec_type(gallivm, type); switch (type.width) { case 8: pcmpeq = "llvm.x86.sse2.pcmpeq.b"; pcmpgt = "llvm.x86.sse2.pcmpgt.b"; break; case 16: pcmpeq = "llvm.x86.sse2.pcmpeq.w"; pcmpgt = "llvm.x86.sse2.pcmpgt.w"; break; case 32: pcmpeq = "llvm.x86.sse2.pcmpeq.d"; pcmpgt = "llvm.x86.sse2.pcmpgt.d"; break; default: assert(0); return lp_build_undef(gallivm, type); } /* There are no unsigned comparison instructions. So flip the sign bit * so that the results match. */ if (table[func].gt && !type.sign) { LLVMValueRef msb = lp_build_const_int_vec(gallivm, type, (unsigned long long)1 << (type.width - 1)); a = LLVMBuildXor(builder, a, msb, ""); b = LLVMBuildXor(builder, b, msb, ""); } if(table[func].swap) { args[0] = b; args[1] = a; } else { args[0] = a; args[1] = b; } if(table[func].eq) res = lp_build_intrinsic(builder, pcmpeq, vec_type, args, 2); else if (table[func].gt) res = lp_build_intrinsic(builder, pcmpgt, vec_type, args, 2); else res = LLVMConstNull(vec_type); if(table[func].not) res = LLVMBuildNot(builder, res, ""); return res; } } /* if (type.width * type.length == 128) */ #endif #endif /* HAVE_LLVM < 0x0207 */ /* XXX: It is not clear if we should use the ordered or unordered operators */ if(type.floating) { LLVMRealPredicate op; switch(func) { case PIPE_FUNC_NEVER: op = LLVMRealPredicateFalse; break; case PIPE_FUNC_ALWAYS: op = LLVMRealPredicateTrue; break; case PIPE_FUNC_EQUAL: op = LLVMRealUEQ; break; case PIPE_FUNC_NOTEQUAL: op = LLVMRealUNE; break; case PIPE_FUNC_LESS: op = LLVMRealULT; break; case PIPE_FUNC_LEQUAL: op = LLVMRealULE; break; case PIPE_FUNC_GREATER: op = LLVMRealUGT; break; case PIPE_FUNC_GEQUAL: op = LLVMRealUGE; break; default: assert(0); return lp_build_undef(gallivm, type); } #if HAVE_LLVM >= 0x0207 cond = LLVMBuildFCmp(builder, op, a, b, ""); res = LLVMBuildSExt(builder, cond, int_vec_type, ""); #else if (type.length == 1) { cond = LLVMBuildFCmp(builder, op, a, b, ""); res = LLVMBuildSExt(builder, cond, int_vec_type, ""); } else { unsigned i; res = LLVMGetUndef(int_vec_type); debug_printf("%s: warning: using slow element-wise float" " vector comparison\n", __FUNCTION__); for (i = 0; i < type.length; ++i) { LLVMValueRef index = lp_build_const_int32(gallivm, i); cond = LLVMBuildFCmp(builder, op, LLVMBuildExtractElement(builder, a, index, ""), LLVMBuildExtractElement(builder, b, index, ""), ""); cond = LLVMBuildSelect(builder, cond, LLVMConstExtractElement(ones, index), LLVMConstExtractElement(zeros, index), ""); res = LLVMBuildInsertElement(builder, res, cond, index, ""); } } #endif } else { LLVMIntPredicate op; switch(func) { case PIPE_FUNC_EQUAL: op = LLVMIntEQ; break; case PIPE_FUNC_NOTEQUAL: op = LLVMIntNE; break; case PIPE_FUNC_LESS: op = type.sign ? LLVMIntSLT : LLVMIntULT; break; case PIPE_FUNC_LEQUAL: op = type.sign ? LLVMIntSLE : LLVMIntULE; break; case PIPE_FUNC_GREATER: op = type.sign ? LLVMIntSGT : LLVMIntUGT; break; case PIPE_FUNC_GEQUAL: op = type.sign ? LLVMIntSGE : LLVMIntUGE; break; default: assert(0); return lp_build_undef(gallivm, type); } #if HAVE_LLVM >= 0x0207 cond = LLVMBuildICmp(builder, op, a, b, ""); res = LLVMBuildSExt(builder, cond, int_vec_type, ""); #else if (type.length == 1) { cond = LLVMBuildICmp(builder, op, a, b, ""); res = LLVMBuildSExt(builder, cond, int_vec_type, ""); } else { unsigned i; res = LLVMGetUndef(int_vec_type); if (gallivm_debug & GALLIVM_DEBUG_PERF) { debug_printf("%s: using slow element-wise int" " vector comparison\n", __FUNCTION__); } for(i = 0; i < type.length; ++i) { LLVMValueRef index = lp_build_const_int32(gallivm, i); cond = LLVMBuildICmp(builder, op, LLVMBuildExtractElement(builder, a, index, ""), LLVMBuildExtractElement(builder, b, index, ""), ""); cond = LLVMBuildSelect(builder, cond, LLVMConstExtractElement(ones, index), LLVMConstExtractElement(zeros, index), ""); res = LLVMBuildInsertElement(builder, res, cond, index, ""); } } #endif } return res; }