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:
Yukihiro "Matz" Matsumoto
2025-09-05 07:23:52 +09:00
parent 7f5904ea94
commit a11a3f2fe6
4 changed files with 1738 additions and 1624 deletions
+58 -95
View File
@@ -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);
+11
View File
@@ -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 */
+100 -30
View File
@@ -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;
}
File diff suppressed because it is too large Load Diff