mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-compiler: implement NODE_BIGINT for optimal integer handling
Replace dual integer parsing paths with two-tier system: - NODE_INT stores int32_t values directly for common case - NODE_BIGINT stores string representation for overflow values - Custom read_int32() function provides locale-independent parsing - Remove unused readint() function from codegen This eliminates confusing dual code paths while maintaining performance for the majority of integer literals that fit in 32-bit range. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3314,51 +3314,6 @@ raise_error(codegen_scope *s, const char *msg)
|
||||
genop_1(s, OP_ERR, idx);
|
||||
}
|
||||
|
||||
static mrb_int
|
||||
readint(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow)
|
||||
{
|
||||
const char *e = p + strlen(p);
|
||||
mrb_int result = 0;
|
||||
|
||||
mrb_assert(base >= 2 && base <= 16);
|
||||
if (*p == '+') p++;
|
||||
while (p < e) {
|
||||
int n;
|
||||
char c = *p;
|
||||
switch (c) {
|
||||
case '0': case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7':
|
||||
n = c - '0'; break;
|
||||
case '8': case '9':
|
||||
n = c - '0'; break;
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
n = c - 'a' + 10; break;
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
n = c - 'A' + 10; break;
|
||||
default:
|
||||
codegen_error(s, "malformed readint input");
|
||||
*overflow = TRUE;
|
||||
/* not reached */
|
||||
return result;
|
||||
}
|
||||
if (mrb_int_mul_overflow(result, base, &result)) {
|
||||
overflow:
|
||||
*overflow = TRUE;
|
||||
return 0;
|
||||
}
|
||||
mrb_uint tmp = ((mrb_uint)result)+n;
|
||||
if (neg && tmp == (mrb_uint)MRB_INT_MAX+1) {
|
||||
*overflow = FALSE;
|
||||
return MRB_INT_MIN;
|
||||
}
|
||||
if (tmp > MRB_INT_MAX) goto overflow;
|
||||
result = (mrb_int)tmp;
|
||||
p++;
|
||||
}
|
||||
*overflow = FALSE;
|
||||
if (neg) return -result;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
gen_retval(codegen_scope *s, node *tree)
|
||||
@@ -3384,6 +3339,8 @@ true_always(node *tree)
|
||||
case NODE_VARIABLE:
|
||||
/* Check variable-sized nodes that are always true */
|
||||
switch (VAR_NODE_TYPE(tree->cdr)) {
|
||||
case NODE_INT:
|
||||
case NODE_BIGINT:
|
||||
case NODE_FLOAT:
|
||||
case NODE_TRUE:
|
||||
return TRUE;
|
||||
@@ -4184,44 +4141,63 @@ codegen_negate(codegen_scope *s, node *tree, int val)
|
||||
#endif
|
||||
|
||||
case NODE_INT:
|
||||
if (val) {
|
||||
char *p = (char*)tree->cdr->car;
|
||||
int base = node_to_int(tree->cdr->cdr->car);
|
||||
mrb_int i;
|
||||
mrb_bool overflow;
|
||||
|
||||
i = readint(s, p, base, TRUE, &overflow);
|
||||
if (overflow) {
|
||||
base = -base;
|
||||
int off = new_litbint(s, p, base);
|
||||
genop_2(s, OP_LOADL, cursp(), off);
|
||||
}
|
||||
else {
|
||||
gen_int(s, cursp(), i);
|
||||
}
|
||||
push();
|
||||
}
|
||||
/* This case should not occur since NODE_INT is now always variable-sized */
|
||||
break;
|
||||
|
||||
case NODE_VARIABLE:
|
||||
{
|
||||
enum node_type vnt = VAR_NODE_TYPE(tree->cdr);
|
||||
switch (vnt) {
|
||||
#ifndef MRB_NO_FLOAT
|
||||
if (VAR_NODE_TYPE(tree->cdr) == NODE_FLOAT) {
|
||||
if (val) {
|
||||
struct mrb_ast_float_node *float_n = (struct mrb_ast_float_node*)tree->cdr;
|
||||
const char *value = float_n->value;
|
||||
double f;
|
||||
case NODE_FLOAT:
|
||||
if (val) {
|
||||
struct mrb_ast_float_node *float_n = (struct mrb_ast_float_node*)tree->cdr;
|
||||
const char *value = float_n->value;
|
||||
double f;
|
||||
|
||||
mrb_read_float(value, NULL, &f);
|
||||
int off = new_lit_float(s, (mrb_float)-f);
|
||||
mrb_read_float(value, NULL, &f);
|
||||
int off = new_lit_float(s, (mrb_float)-f);
|
||||
|
||||
gen_load_lit(s, off);
|
||||
}
|
||||
gen_load_lit(s, off);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case NODE_INT:
|
||||
if (val) {
|
||||
int32_t value = INT_NODE_VALUE(tree->cdr);
|
||||
if (value == INT32_MIN) {
|
||||
/* -INT32_MIN overflows, use bigint */
|
||||
int off = new_litbint(s, "2147483648", -10);
|
||||
genop_2(s, OP_LOADL, cursp(), off);
|
||||
}
|
||||
else {
|
||||
gen_int(s, cursp(), -value);
|
||||
}
|
||||
push();
|
||||
}
|
||||
break;
|
||||
|
||||
case NODE_BIGINT:
|
||||
if (val) {
|
||||
char *str = BIGINT_NODE_STRING(tree->cdr);
|
||||
int base = BIGINT_NODE_BASE(tree->cdr);
|
||||
/* Negate base to indicate negative number */
|
||||
int off = new_litbint(s, str, -base);
|
||||
genop_2(s, OP_LOADL, cursp(), off);
|
||||
push();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Fall through to default case */
|
||||
goto default_negate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* fall through for other variable node types */
|
||||
|
||||
default:
|
||||
default_negate:
|
||||
codegen(s, tree, VAL);
|
||||
pop();
|
||||
push_n(2);pop_n(2); /* space for receiver&block */
|
||||
@@ -4234,26 +4210,6 @@ codegen_negate(codegen_scope *s, node *tree, int val)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
codegen_int(codegen_scope *s, node *tree, int val)
|
||||
{
|
||||
if (!val) return;
|
||||
char *p = (char*)tree->car;
|
||||
int base = node_to_int(tree->cdr->car);
|
||||
mrb_int i;
|
||||
mrb_bool overflow;
|
||||
|
||||
i = readint(s, p, base, FALSE, &overflow);
|
||||
if (overflow) {
|
||||
int off = new_litbint(s, p, base);
|
||||
genop_2(s, OP_LOADL, cursp(), off);
|
||||
}
|
||||
else {
|
||||
gen_int(s, cursp(), i);
|
||||
}
|
||||
push();
|
||||
}
|
||||
|
||||
static void
|
||||
codegen_xstr(codegen_scope *s, node *tree, int val)
|
||||
{
|
||||
@@ -6022,6 +5978,16 @@ codegen_variable_node(codegen_scope *s, node *varnode, int val)
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case NODE_BIGINT:
|
||||
if (val) {
|
||||
char *str = BIGINT_NODE_STRING(varnode);
|
||||
int base = BIGINT_NODE_BASE(varnode);
|
||||
int off = new_litbint(s, str, base);
|
||||
genop_2(s, OP_LOADL, cursp(), off);
|
||||
push();
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case NODE_SYM:
|
||||
{
|
||||
int i = new_sym(s, SYM_NODE_VALUE(varnode));
|
||||
@@ -6506,9 +6472,6 @@ codegen(codegen_scope *s, node *tree, int val)
|
||||
codegen_block_arg(s, tree, val);
|
||||
break;
|
||||
|
||||
case NODE_INT:
|
||||
codegen_int(s, tree, val);
|
||||
break;
|
||||
|
||||
case NODE_NEGATE:
|
||||
codegen_negate(s, tree, val);
|
||||
|
||||
@@ -53,6 +53,7 @@ enum node_type {
|
||||
NODE_BACK_REF,
|
||||
NODE_MATCH,
|
||||
NODE_INT,
|
||||
NODE_BIGINT,
|
||||
NODE_FLOAT,
|
||||
NODE_NEGATE,
|
||||
NODE_LAMBDA,
|
||||
@@ -195,6 +196,13 @@ struct mrb_ast_int_node {
|
||||
int32_t value; /* Direct 32-bit integer storage */
|
||||
};
|
||||
|
||||
/* Variable-sized big integer node */
|
||||
struct mrb_ast_bigint_node {
|
||||
struct mrb_ast_var_header header; /* 8 bytes */
|
||||
char *string; /* String representation of big number */
|
||||
int base; /* Number base (8, 10, 16) */
|
||||
};
|
||||
|
||||
/* Variable-sized node for variables (lvar, ivar, etc.) */
|
||||
struct mrb_ast_var_node {
|
||||
struct mrb_ast_var_header header;
|
||||
@@ -395,6 +403,7 @@ struct mrb_ast_super_node {
|
||||
#define sym_node(n) ((struct mrb_ast_sym_node*)(n))
|
||||
#define str_node(n) ((struct mrb_ast_str_node*)(n))
|
||||
#define int_node(n) ((struct mrb_ast_int_node*)(n))
|
||||
#define bigint_node(n) ((struct mrb_ast_bigint_node*)(n))
|
||||
#define var_node(n) ((struct mrb_ast_var_node*)(n))
|
||||
|
||||
/* Phase 2 node casting macros */
|
||||
@@ -427,6 +436,8 @@ struct mrb_ast_super_node {
|
||||
#define STR_NODE_LEN(n) (str_node(n)->len)
|
||||
#define STR_NODE_INLINE_P(n) (var_header(n)->flags & VAR_NODE_FLAG_INLINE_DATA)
|
||||
#define INT_NODE_VALUE(n) (int_node(n)->value)
|
||||
#define BIGINT_NODE_STRING(n) (bigint_node(n)->string)
|
||||
#define BIGINT_NODE_BASE(n) (bigint_node(n)->base)
|
||||
#define VAR_NODE_SYMBOL(n) (var_node(n)->symbol)
|
||||
|
||||
/* Phase 2 value access macros */
|
||||
|
||||
@@ -1931,11 +1931,24 @@ new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
|
||||
return list4((node*)NODE_OP_ASGN, a, sym_to_node(op), b);
|
||||
}
|
||||
|
||||
static node*
|
||||
new_int_n(parser_state *p, int32_t val)
|
||||
{
|
||||
size_t size = sizeof(struct mrb_ast_int_node);
|
||||
enum mrb_ast_size_class class = size_to_class(size);
|
||||
struct mrb_ast_int_node *n = (struct mrb_ast_int_node*)parser_alloc_var(p, size, class);
|
||||
|
||||
init_var_header(&n->header, p, NODE_INT, class);
|
||||
n->value = val;
|
||||
|
||||
return cons_head((node*)NODE_VARIABLE, (node*)n);
|
||||
}
|
||||
|
||||
static node*
|
||||
new_imaginary(parser_state *p, node *imaginary)
|
||||
{
|
||||
return new_fcall(p, MRB_SYM_2(p->mrb, Complex),
|
||||
new_callargs(p, list2(list3((node*)NODE_INT, (node*)strdup("0"), int_to_node(10)), imaginary), 0, 0));
|
||||
new_callargs(p, list2(new_int_n(p, 0), imaginary), 0, 0));
|
||||
}
|
||||
|
||||
static node*
|
||||
@@ -1944,55 +1957,112 @@ new_rational(parser_state *p, node *rational)
|
||||
return new_fcall(p, MRB_SYM_2(p->mrb, Rational), new_callargs(p, list1(rational), 0, 0));
|
||||
}
|
||||
|
||||
/* (:int . i) */
|
||||
static node*
|
||||
new_int_original(parser_state *p, const char *s, int base)
|
||||
/* Read integer into int32_t with overflow detection */
|
||||
static mrb_bool
|
||||
read_int32(const char *p, int base, int32_t *result)
|
||||
{
|
||||
return list3((node*)NODE_INT, (node*)strdup(s), int_to_node(base));
|
||||
}
|
||||
const char *e = p + strlen(p);
|
||||
int32_t value = 0;
|
||||
mrb_bool neg = FALSE;
|
||||
|
||||
static node*
|
||||
new_int_var(parser_state *p, int32_t value)
|
||||
{
|
||||
size_t size = sizeof(struct mrb_ast_int_node);
|
||||
enum mrb_ast_size_class class = size_to_class(size);
|
||||
if (base < 2 || base > 16) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct mrb_ast_int_node *n = (struct mrb_ast_int_node*)parser_alloc_var(p, size, class);
|
||||
if (*p == '+') {
|
||||
p++;
|
||||
}
|
||||
else if (*p == '-') {
|
||||
neg = TRUE;
|
||||
p++;
|
||||
}
|
||||
|
||||
init_var_header(&n->header, p, NODE_INT, class);
|
||||
n->value = value;
|
||||
while (p < e) {
|
||||
int n;
|
||||
char c = *p;
|
||||
|
||||
return cons_head((node*)NODE_VARIABLE, (node*)n);
|
||||
/* Skip underscores */
|
||||
if (c == '_') {
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Parse digit */
|
||||
if (c >= '0' && c <= '9') {
|
||||
n = c - '0';
|
||||
}
|
||||
else if (c >= 'a' && c <= 'f') {
|
||||
n = c - 'a' + 10;
|
||||
}
|
||||
else if (c >= 'A' && c <= 'F') {
|
||||
n = c - 'A' + 10;
|
||||
}
|
||||
else {
|
||||
/* Invalid character */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (n >= base) {
|
||||
/* Digit not valid for this base */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Check for multiplication overflow */
|
||||
if (value > INT32_MAX / base) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
value *= base;
|
||||
|
||||
/* Check for addition overflow */
|
||||
if (value > INT32_MAX - n) {
|
||||
/* Special case: -INT32_MIN is valid */
|
||||
if (neg && value == (INT32_MAX - n + 1) && p + 1 == e) {
|
||||
*result = INT32_MIN;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
value += n;
|
||||
p++;
|
||||
}
|
||||
|
||||
*result = neg ? -value : value;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static node*
|
||||
new_int(parser_state *p, const char *s, int base, int suffix)
|
||||
{
|
||||
int32_t val;
|
||||
node* result;
|
||||
|
||||
if (p->var_nodes_enabled && suffix == 0) {
|
||||
char *e;
|
||||
long long val;
|
||||
#ifdef MRB_INT64
|
||||
val = strtoll(s, &e, base);
|
||||
#else
|
||||
val = strtol(s, &e, base);
|
||||
#endif
|
||||
if (*e == 0) { /* conversion successful */
|
||||
if (val >= INT32_MIN && val <= INT32_MAX) {
|
||||
return new_int_var(p, (int32_t)val);
|
||||
}
|
||||
}
|
||||
/* fallback to original for large numbers or parse errors */
|
||||
/* Try to parse as int32_t first */
|
||||
if (read_int32(s, base, &val)) {
|
||||
result = new_int_n(p, val);
|
||||
}
|
||||
else {
|
||||
/* Big integer - create NODE_BIGINT */
|
||||
size_t size = sizeof(struct mrb_ast_bigint_node);
|
||||
enum mrb_ast_size_class class = size_to_class(size);
|
||||
struct mrb_ast_bigint_node *n = (struct mrb_ast_bigint_node*)parser_alloc_var(p, size, class);
|
||||
|
||||
init_var_header(&n->header, p, NODE_BIGINT, class);
|
||||
n->string = strdup(s);
|
||||
n->base = base;
|
||||
|
||||
result = cons_head((node*)NODE_VARIABLE, (node*)n);
|
||||
}
|
||||
|
||||
result = new_int_original(p, s, base);
|
||||
/* Handle suffix modifiers */
|
||||
if (suffix & NUM_SUFFIX_R) {
|
||||
result = new_rational(p, result);
|
||||
}
|
||||
if (suffix & NUM_SUFFIX_I) {
|
||||
result = new_imaginary(p, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+1569
-1499
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user