+ /* short circuit evaluation */
+ ir_block *other, *merge;
+ ir_block *from_left, *from_right;
+ ir_instr *phi;
+ size_t merge_id;
+
+ /* prepare end-block */
+ merge_id = vec_size(func->ir_func->blocks);
+ merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_merge"));
+
+ /* generate the left expression */
+ cgen = self->left->codegen;
+ if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
+ return false;
+ /* remember the block */
+ from_left = func->curblock;
+
+ /* create a new block for the right expression */
+ other = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_other"));
+ if (self->op == INSTR_AND) {
+ /* on AND: left==true -> other */
+ if (!ir_block_create_if(func->curblock, ast_ctx(self), left, other, merge))
+ return false;
+ } else {
+ /* on OR: left==false -> other */
+ if (!ir_block_create_if(func->curblock, ast_ctx(self), left, merge, other))
+ return false;
+ }
+ /* use the likely flag */
+ vec_last(func->curblock->instr)->likely = true;
+
+ /* enter the right-expression's block */
+ func->curblock = other;
+ /* generate */
+ cgen = self->right->codegen;
+ if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
+ return false;
+ /* remember block */
+ from_right = func->curblock;
+
+ /* jump to the merge block */
+ if (!ir_block_create_jump(func->curblock, ast_ctx(self), merge))
+ return false;
+
+ vec_remove(func->ir_func->blocks, merge_id, 1);
+ vec_push(func->ir_func->blocks, merge);
+
+ func->curblock = merge;
+ phi = ir_block_create_phi(func->curblock, ast_ctx(self),
+ ast_function_label(func, "sce_value"),
+ self->expression.vtype);
+ ir_phi_add(phi, from_left, left);
+ ir_phi_add(phi, from_right, right);
+ *out = ir_phi_value(phi);
+ if (!*out)
+ return false;
+
+ if (!OPTS_FLAG(PERL_LOGIC)) {
+ /* cast-to-bool */
+ if (OPTS_FLAG(CORRECT_LOGIC) && (*out)->vtype == TYPE_VECTOR) {
+ *out = ir_block_create_unary(func->curblock, ast_ctx(self),
+ ast_function_label(func, "sce_bool_v"),
+ INSTR_NOT_V, *out);
+ if (!*out)
+ return false;
+ *out = ir_block_create_unary(func->curblock, ast_ctx(self),
+ ast_function_label(func, "sce_bool"),
+ INSTR_NOT_F, *out);
+ if (!*out)
+ return false;
+ }
+ else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && (*out)->vtype == TYPE_STRING) {
+ *out = ir_block_create_unary(func->curblock, ast_ctx(self),
+ ast_function_label(func, "sce_bool_s"),
+ INSTR_NOT_S, *out);
+ if (!*out)
+ return false;
+ *out = ir_block_create_unary(func->curblock, ast_ctx(self),
+ ast_function_label(func, "sce_bool"),
+ INSTR_NOT_F, *out);
+ if (!*out)
+ return false;
+ }
+ else {
+ *out = ir_block_create_binop(func->curblock, ast_ctx(self),
+ ast_function_label(func, "sce_bool"),
+ INSTR_AND, *out, *out);
+ if (!*out)
+ return false;
+ }
+ }
+
+ self->expression.outr = *out;
+ codegen_output_type(self, *out);
+ return true;
+ }
+
+ if (self->right_first) {
+ cgen = self->right->codegen;
+ if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
+ return false;
+ cgen = self->left->codegen;
+ if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
+ return false;
+ } else {
+ cgen = self->left->codegen;
+ if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
+ return false;
+ cgen = self->right->codegen;
+ if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
+ return false;
+ }