static sljit_s32 emit_op()

in pcre/sljit/sljitNativeARM_32.c [1602:1790]


static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 inp_flags,
	sljit_s32 dst, sljit_sw dstw,
	sljit_s32 src1, sljit_sw src1w,
	sljit_s32 src2, sljit_sw src2w)
{
	/* arg1 goes to TMP_REG1 or src reg
	   arg2 goes to TMP_REG2, imm or src reg
	   TMP_REG3 can be used for caching
	   result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */

	/* We prefers register and simple consts. */
	sljit_s32 dst_r;
	sljit_s32 src1_r;
	sljit_s32 src2_r = 0;
	sljit_s32 sugg_src2_r = TMP_REG2;
	sljit_s32 flags = GET_FLAGS(op) ? SET_FLAGS : 0;

	compiler->cache_arg = 0;
	compiler->cache_argw = 0;

	/* Destination check. */
	if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) {
		if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32 && !(src2 & SLJIT_MEM))
			return SLJIT_SUCCESS;
		dst_r = TMP_REG2;
	}
	else if (FAST_IS_REG(dst)) {
		dst_r = dst;
		flags |= REG_DEST;
		if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32)
			sugg_src2_r = dst_r;
	}
	else {
		SLJIT_ASSERT(dst & SLJIT_MEM);
		if (getput_arg_fast(compiler, inp_flags | ARG_TEST, TMP_REG2, dst, dstw)) {
			flags |= FAST_DEST;
			dst_r = TMP_REG2;
		}
		else {
			flags |= SLOW_DEST;
			dst_r = 0;
		}
	}

	/* Source 1. */
	if (FAST_IS_REG(src1))
		src1_r = src1;
	else if (FAST_IS_REG(src2)) {
		flags |= ARGS_SWAPPED;
		src1_r = src2;
		src2 = src1;
		src2w = src1w;
	}
	else do { /* do { } while(0) is used because of breaks. */
		src1_r = 0;
		if ((inp_flags & ALLOW_ANY_IMM) && (src1 & SLJIT_IMM)) {
			/* The second check will generate a hit. */
			src2_r = get_imm(src1w);
			if (src2_r) {
				flags |= ARGS_SWAPPED;
				src1 = src2;
				src1w = src2w;
				break;
			}
			if (inp_flags & ALLOW_INV_IMM) {
				src2_r = get_imm(~src1w);
				if (src2_r) {
					flags |= ARGS_SWAPPED | INV_IMM;
					src1 = src2;
					src1w = src2w;
					break;
				}
			}
			if (GET_OPCODE(op) == SLJIT_ADD) {
				src2_r = get_imm(-src1w);
				if (src2_r) {
					/* Note: ARGS_SWAPPED is intentionally not applied! */
					src1 = src2;
					src1w = src2w;
					op = SLJIT_SUB | GET_ALL_FLAGS(op);
					break;
				}
			}
		}

		if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w)) {
			FAIL_IF(compiler->error);
			src1_r = TMP_REG1;
		}
	} while (0);

	/* Source 2. */
	if (src2_r == 0) {
		if (FAST_IS_REG(src2)) {
			src2_r = src2;
			flags |= REG_SOURCE;
			if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_S32)
				dst_r = src2_r;
		}
		else do { /* do { } while(0) is used because of breaks. */
			if ((inp_flags & ALLOW_ANY_IMM) && (src2 & SLJIT_IMM)) {
				src2_r = get_imm(src2w);
				if (src2_r)
					break;
				if (inp_flags & ALLOW_INV_IMM) {
					src2_r = get_imm(~src2w);
					if (src2_r) {
						flags |= INV_IMM;
						break;
					}
				}
				if (GET_OPCODE(op) == SLJIT_ADD) {
					src2_r = get_imm(-src2w);
					if (src2_r) {
						op = SLJIT_SUB | GET_ALL_FLAGS(op);
						flags &= ~ARGS_SWAPPED;
						break;
					}
				}
				if (GET_OPCODE(op) == SLJIT_SUB && !(flags & ARGS_SWAPPED)) {
					src2_r = get_imm(-src2w);
					if (src2_r) {
						op = SLJIT_ADD | GET_ALL_FLAGS(op);
						flags &= ~ARGS_SWAPPED;
						break;
					}
				}
			}

			/* src2_r is 0. */
			if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w)) {
				FAIL_IF(compiler->error);
				src2_r = sugg_src2_r;
			}
		} while (0);
	}

	/* src1_r, src2_r and dst_r can be zero (=unprocessed) or non-zero.
	   If they are zero, they must not be registers. */
	if (src1_r == 0 && src2_r == 0 && dst_r == 0) {
		if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
			SLJIT_ASSERT(!(flags & ARGS_SWAPPED));
			flags |= ARGS_SWAPPED;
			FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src2, src2w, src1, src1w));
			FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src1, src1w, dst, dstw));
		}
		else {
			FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
			FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw));
		}
		src1_r = TMP_REG1;
		src2_r = TMP_REG2;
	}
	else if (src1_r == 0 && src2_r == 0) {
		FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
		src1_r = TMP_REG1;
	}
	else if (src1_r == 0 && dst_r == 0) {
		FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
		src1_r = TMP_REG1;
	}
	else if (src2_r == 0 && dst_r == 0) {
		FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw));
		src2_r = sugg_src2_r;
	}

	if (dst_r == 0)
		dst_r = TMP_REG2;

	if (src1_r == 0) {
		FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, 0, 0));
		src1_r = TMP_REG1;
	}

	if (src2_r == 0) {
		FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, 0, 0));
		src2_r = sugg_src2_r;
	}

	FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));

	if (flags & (FAST_DEST | SLOW_DEST)) {
		if (flags & FAST_DEST)
			FAIL_IF(getput_arg_fast(compiler, inp_flags, dst_r, dst, dstw));
		else
			FAIL_IF(getput_arg(compiler, inp_flags, dst_r, dst, dstw, 0, 0));
	}
	return SLJIT_SUCCESS;
}