static void draw_select_vec4(ivl_expr_t expr) { // This is the sub-expression to part-select. ivl_expr_t subexpr = ivl_expr_oper1(expr); // This is the base of the part select ivl_expr_t base = ivl_expr_oper2(expr); // This is the part select width unsigned wid = ivl_expr_width(expr); // Is the select base expression signed or unsigned? char sign_suff = ivl_expr_signed(base)? 's' : 'u'; // Special Case: If the sub-expression is a STRING, then this // is a select from that string. if (ivl_expr_value(subexpr)==IVL_VT_STRING) { assert(base); assert(wid==8); draw_eval_string(subexpr); int base_idx = allocate_word(); draw_eval_expr_into_integer(base, base_idx); fprintf(vvp_out, " %%substr/vec4 %d, %u;\n", base_idx, wid); fprintf(vvp_out, " %%pop/str 1;\n"); clr_word(base_idx); return; } if (ivl_expr_value(subexpr)==IVL_VT_DARRAY) { ivl_signal_t sig = ivl_expr_signal(subexpr); assert(sig); assert( (ivl_signal_data_type(sig)==IVL_VT_DARRAY) || (ivl_signal_data_type(sig)==IVL_VT_QUEUE) ); assert(base); draw_eval_expr_into_integer(base, 3); fprintf(vvp_out, " %%load/dar/vec4 v%p_0;\n", sig); return; } if (test_immediate_vec4_ok(base)) { unsigned long val0, valx; unsigned base_wid; make_immediate_vec4_words(base, &val0, &valx, &base_wid); assert(valx == 0); draw_eval_vec4(subexpr); fprintf(vvp_out, " %%parti/%c %u, %lu, %u;\n", sign_suff, wid, val0, base_wid); } else { draw_eval_vec4(subexpr); draw_eval_vec4(base); fprintf(vvp_out, " %%part/%c %u;\n", sign_suff, wid); } }
static void draw_binary_vec4_arith(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); unsigned lwid = ivl_expr_width(le); unsigned rwid = ivl_expr_width(re); unsigned ewid = ivl_expr_width(expr); int signed_flag = ivl_expr_signed(le) && ivl_expr_signed(re) ? 1 : 0; const char*signed_string = signed_flag? "/s" : ""; /* All the arithmetic operations handled here require that the operands (and the result) be the same width. We further assume that the core has not given us an operand wider then the expression width. So padd operands as needed. */ draw_eval_vec4(le); if (lwid != ewid) { fprintf(vvp_out, " %%pad/%c %u;\n", ivl_expr_signed(le)? 's' : 'u', ewid); } /* Special case: If the re expression can be collected into an immediate operand, and the instruction supports it, then generate an immediate instruction instead of the generic version. */ if (rwid==ewid && test_immediate_vec4_ok(re)) { switch (ivl_expr_opcode(expr)) { case '+': draw_immediate_vec4(re, "%addi"); return; case '-': draw_immediate_vec4(re, "%subi"); return; case '*': draw_immediate_vec4(re, "%muli"); return; default: break; } } draw_eval_vec4(re); if (rwid != ewid) { fprintf(vvp_out, " %%pad/%c %u;\n", ivl_expr_signed(re)? 's' : 'u', ewid); } switch (ivl_expr_opcode(expr)) { case '+': fprintf(vvp_out, " %%add;\n"); break; case '-': fprintf(vvp_out, " %%sub;\n"); break; case '*': fprintf(vvp_out, " %%mul;\n"); break; case '/': fprintf(vvp_out, " %%div%s;\n", signed_string); break; case '%': fprintf(vvp_out, " %%mod%s;\n", signed_string); break; case 'p': /* Note that the power operator is signed if EITHER of the operands is signed. This is different from other arithmetic operators. */ if (ivl_expr_signed(le) || ivl_expr_signed(re)) signed_string = "/s"; fprintf(vvp_out, " %%pow%s;\n", signed_string); break; default: assert(0); break; } }
static void draw_binary_vec4_le(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); ivl_expr_t tmp; if ((ivl_expr_value(le) == IVL_VT_REAL) || (ivl_expr_value(re) == IVL_VT_REAL)) { draw_binary_vec4_le_real(expr); return; } char use_opcode = ivl_expr_opcode(expr); char s_flag = (ivl_expr_signed(le) && ivl_expr_signed(re)) ? 's' : 'u'; /* If this is a > or >=, then convert it to < or <= by swapping the operands. Adjust the opcode to match. */ switch (use_opcode) { case 'G': tmp = le; le = re; re = tmp; use_opcode = 'L'; break; case '>': tmp = le; le = re; re = tmp; use_opcode = '<'; break; } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { draw_binary_vec4_le_string(expr); return; } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_type(re)==IVL_EX_STRING)) { draw_binary_vec4_le_string(expr); return; } if ((ivl_expr_type(le)==IVL_EX_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { draw_binary_vec4_le_string(expr); return; } /* NOTE: I think I would rather the elaborator handle the operand widths. When that happens, take this code out. */ unsigned use_wid = ivl_expr_width(le); if (ivl_expr_width(re) > use_wid) use_wid = ivl_expr_width(re); draw_eval_vec4(le); resize_vec4_wid(le, use_wid); if (ivl_expr_width(re)==use_wid && test_immediate_vec4_ok(re)) { /* Special case: If the right operand can be handled as an immediate operand, then use that instead. */ char opcode[8]; snprintf(opcode, sizeof opcode, "%%cmpi/%c", s_flag); draw_immediate_vec4(re, opcode); } else { draw_eval_vec4(re); resize_vec4_wid(re, use_wid); fprintf(vvp_out, " %%cmp/%c;\n", s_flag); } switch (use_opcode) { case 'L': fprintf(vvp_out, " %%flag_get/vec4 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 5;\n"); fprintf(vvp_out, " %%or;\n"); break; case '<': fprintf(vvp_out, " %%flag_get/vec4 5;\n"); break; default: assert(0); break; } }
static int draw_condition_binary_compare(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); if ((ivl_expr_value(le) == IVL_VT_REAL) || (ivl_expr_value(re) == IVL_VT_REAL)) { return draw_condition_fallback(expr); } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { return draw_condition_fallback(expr); } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_type(re)==IVL_EX_STRING)) { return draw_condition_fallback(expr); } if ((ivl_expr_type(le)==IVL_EX_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { return draw_condition_fallback(expr); } if ((ivl_expr_value(le)==IVL_VT_CLASS) && (ivl_expr_value(re)==IVL_VT_CLASS)) { return draw_condition_fallback(expr); } unsigned use_wid = ivl_expr_width(le); if (ivl_expr_width(re) > use_wid) use_wid = ivl_expr_width(re); /* If the le is constant, then swap the operands so that we can possibly take advantage of the immediate version of the %cmp instruction. */ if (ivl_expr_width(le)==use_wid && test_immediate_vec4_ok(le)) { ivl_expr_t tmp = le; re = le; le = tmp; } draw_eval_vec4(le); resize_vec4_wid(le, use_wid); char use_opcode = ivl_expr_opcode(expr); if (ivl_expr_width(re)==use_wid && test_immediate_vec4_ok(re)) { /* Special case: If the right operand can be handled as an immediate operand, then use that instead. */ if (use_opcode=='n' || use_opcode=='N') draw_immediate_vec4(re, "%cmpi/ne"); else draw_immediate_vec4(re, "%cmpi/e"); } else { draw_eval_vec4(re); resize_vec4_wid(re, use_wid); if (use_opcode=='n' || use_opcode=='N') fprintf(vvp_out, " %%cmp/ne;\n"); else fprintf(vvp_out, " %%cmp/e;\n"); } switch (ivl_expr_opcode(expr)) { case 'n': /* != */ case 'e': /* == */ return 4; break; case 'N': /* !== */ case 'E': /* === */ return 6; default: assert(0); return -1; } }