<commit_before>#include <string.h>
#include <stdlib.h>
#include <assert.h>

#include "../defs.h"
#include "ops.h"
#include "expr_op.h"
#include "../out/lbl.h"
#include "../out/asm.h"
#include "../type_is.h"
#include "../type_nav.h"

#define BOOLEAN_TYPE type_int

const char *str_expr_op()
{
	return "op";
}

#define addr_multiply(i, addr_type)  \
do{                                  \
	type *next = type_next(addr_type); \
	sintegral_t step = 1;              \
                                     \
	if(next)                           \
		step = type_size(next, NULL);    \
                                     \
	i *= step;                         \
}while(0)

static void const_op_num_fp(
		expr *e, consty *k,
		const consty *lhs, const consty *rhs)
{
	/* float const-op */
	floating_t fp_r;

	assert(lhs->type == CONST_NUM && rhs->type == CONST_NUM);

	fp_r = const_op_exec_fp(
			lhs->bits.num.val.f,
			e->rhs ? &rhs->bits.num.val.f : 0,
			e->op);

	k->type = CONST_NUM;

	/* both relational and normal ops between floats are not constant */
	if(!k->nonstandard_const)
		k->nonstandard_const = e;

	if(op_returns_bool(e->op)){
		k->bits.num.val.i = fp_r; /* convert to bool */

	}else{
		const btype *ty = type_get_type(e->tree_type);

		UCC_ASSERT(ty, "no float type for float op?");

		k->bits.num.val.f = fp_r;

		switch(ty->primitive){
			case type_float:   k->bits.num.suffix = VAL_FLOAT;   break;
			case type_double:  k->bits.num.suffix = VAL_DOUBLE;  break;
			case type_ldouble: k->bits.num.suffix = VAL_LDOUBLE; break;
			default: ICE("bad float");
		}
	}
}

typedef struct
{
	int is_lbl;
	union
	{
		integral_t i;
		const char *lbl;
	} bits;
} collapsed_consty;

static void collapse_const(collapsed_consty *out, const consty *in)
{
	switch(in->type){
		case CONST_NO:
			assert(0);

		case CONST_NUM:
			out->is_lbl = 0;
			out->bits.i = in->bits.num.val.i;
			break;

		case CONST_STRK:
			out->is_lbl = 1;
			out->bits.lbl = in->bits.str->lit->lbl;
			break;

		case CONST_NEED_ADDR:
		case CONST_ADDR:
			if(in->bits.addr.is_lbl){
				out->is_lbl = 1;
				out->bits.lbl = in->bits.addr.bits.lbl;
			}else{
				out->is_lbl = 0;
				out->bits.i = in->bits.addr.bits.memaddr;
			}
			break;
	}
}

static void const_op_num_int(
		expr *e, consty *k,
		const consty *lhs, const consty *rhs)
{
	const char *err = NULL;
	int is_signed;
	collapsed_consty l, r;

	/* the op is signed if an operand is, not the result,
	 * e.g. u_a < u_b produces a bool (signed) */
	is_signed = type_is_signed(e->lhs->tree_type) ||
		(e->rhs ? type_is_signed(e->rhs->tree_type) : 0);

	collapse_const(&l, lhs);
	collapse_const(&r, rhs);

	CONST_FOLD_LEAF(k);
	switch(l.is_lbl + r.is_lbl){
		default:
			assert(0);

		case 1:
			k->type = CONST_NO;
			break;

		case 0:
		{
			integral_t int_r = const_op_exec(
					l.bits.i, &r.bits.i,
					e->op, is_signed, &err);

			if(err){
				warn_at(&e->where, "%s", err);
				k->type = CONST_NO;
			}else{
				k->type = CONST_NUM;
				k->bits.num.val.i = int_r;
			}
			break;
		}

		case 2:
			k->type = CONST_NUM;
			k->bits.num.val.i = !strcmp(l.bits.lbl, r.bits.lbl);
			break;
	}
}

static void const_op_num(
		expr *e, consty *k,
		const consty *lhs, const consty *rhs)
{
	int fp[2] = {<commit_msg>Handle pointer stepping in constant additions<commit_after>#include <string.h>
#include <stdlib.h>
#include <assert.h>

#include "../defs.h"
#include "ops.h"
#include "expr_op.h"
#include "../out/lbl.h"
#include "../out/asm.h"
#include "../type_is.h"
#include "../type_nav.h"

#define BOOLEAN_TYPE type_int

const char *str_expr_op()
{
	return "op";
}

#define addr_multiply(i, addr_type)  \
do{                                  \
	type *next = type_next(addr_type); \
	sintegral_t step = 1;              \
                                     \
	if(next)                           \
		step = type_size(next, NULL);    \
                                     \
	i *= step;                         \
}while(0)

static void const_op_num_fp(
		expr *e, consty *k,
		const consty *lhs, const consty *rhs)
{
	/* float const-op */
	floating_t fp_r;

	assert(lhs->type == CONST_NUM && rhs->type == CONST_NUM);

	fp_r = const_op_exec_fp(
			lhs->bits.num.val.f,
			e->rhs ? &rhs->bits.num.val.f : 0,
			e->op);

	k->type = CONST_NUM;

	/* both relational and normal ops between floats are not constant */
	if(!k->nonstandard_const)
		k->nonstandard_const = e;

	if(op_returns_bool(e->op)){
		k->bits.num.val.i = fp_r; /* convert to bool */

	}else{
		const btype *ty = type_get_type(e->tree_type);

		UCC_ASSERT(ty, "no float type for float op?");

		k->bits.num.val.f = fp_r;

		switch(ty->primitive){
			case type_float:   k->bits.num.suffix = VAL_FLOAT;   break;
			case type_double:  k->bits.num.suffix = VAL_DOUBLE;  break;
			case type_ldouble: k->bits.num.suffix = VAL_LDOUBLE; break;
			default: ICE("bad float");
		}
	}
}

typedef struct
{
	int is_lbl;
	union
	{
		integral_t i;
		const char *lbl;
	} bits;
} collapsed_consty;

static void collapse_const(collapsed_consty *out, const consty *in)
{
	switch(in->type){
		case CONST_NO:
			assert(0);

		case CONST_NUM:
			out->is_lbl = 0;
			out->bits.i = in->bits.num.val.i;
			break;

		case CONST_STRK:
			out->is_lbl = 1;
			out->bits.lbl = in->bits.str->lit->lbl;
			break;

		case CONST_NEED_ADDR:
		case CONST_ADDR:
			if(in->bits.addr.is_lbl){
				out->is_lbl = 1;
				out->bits.lbl = in->bits.addr.bits.lbl;
			}else{
				out->is_lbl = 0;
				out->bits.i = in->bits.addr.bits.memaddr;
			}
			break;
	}
}

static void const_op_num_int(
		expr *e, consty *k,
		const consty *lhs, const consty *rhs)
{
	const char *err = NULL;
	int is_signed;
	collapsed_consty l, r;

	/* the op is signed if an operand is, not the result,
	 * e.g. u_a < u_b produces a bool (signed) */
	is_signed = type_is_signed(e->lhs->tree_type) ||
		(e->rhs ? type_is_signed(e->rhs->tree_type) : 0);

	collapse_const(&l, lhs);
	collapse_const(&r, rhs);

	CONST_FOLD_LEAF(k);
	switch(l.is_lbl + r.is_lbl){
		default:
			assert(0);

		case 1:
			k->type = CONST_NO;
			break;

		case 0:
		{
			integral_t int_r;
			type *ptr;
			int ptr_r = 0;

			/* need to apply pointer arithmetic if +/- */
			if((e->op == op_plus || e->op == op_minus)
			&& ((ptr = type_is_ptr(e->lhs->tree_type))
			|| (ptr_r = 1, ptr = type_is_ptr(e->rhs->tree_type))))
			{
				unsigned step = type_size(ptr, &e->where);

				*(ptr_r ? &l.bits.i : &r.bits.i) *= step;
			}

			int_r = const_op_exec(
					l.bits.i, &r.bits.i,
					e->op, is_signed, &err);

			if(err){
				warn_at(&e->where, "%s", err);
				k->type = CONST_NO;
			}else{
				k->type = CONST_NUM;
				k->bits.num.val.i = int_r;
			}
			break;
		}

		case 2:
			k->type = CONST_NUM;
			k->bits.num.val.i = !strcmp(l.bits.lbl, r.bits.lbl);
			break;
	}
}

static void const_op_num(
		expr *e, consty *k,
		const consty *lhs, const consty *rhs)
{
	int fp[2] = {
{'fraction_non_alphanumeric': 0.16276022710729363, 'fraction_numerical': 0.004367447954578541, 'mean_word_length': 2.4820070957932083, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 108, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1319231', 'n_tokens_mistral': 3169, 'n_tokens_neox': 2863, 'n_words': 601}