Skip to content

Commit 5a48c04

Browse files
feat(soroban): (U)int 256 support (#1831) (#1832)
### Description Soroban contracts increasingly require 256-bit arithmetic compatibility with Solidity. This PR brings Solang’s Soroban backend to parity for 256-bit types, enabling broader contract portability and correctness. #### Implementation - - Encoding/Decoding - Split 256-bit values into four 64-bit pieces: lo_lo (0–63), lo_hi (64–127), hi_lo (128–191), hi_hi (192–255) - Preserve signedness during shifts for `int256` - Reconstruct from host-provided 64-bit pieces - Host Functions - Declare and use new host functions: - U256: `ObjToU256LoLo`, `ObjToU256LoHi`, `ObjToU256HiLo`, `ObjToU256HiHi`, `ObjFromU256Pieces` - I256: `ObjToI256LoLo`, `ObjToI256LoHi`, `ObjToI256HiLo`, `ObjToI256HiHi`, `ObjFromI256Pieces` - Provide correct LLVM signatures: - `ObjTo*`: `(i64) -> i64` - `ObjFrom*Pieces`: `(i64, i64, i64, i64) -> i64` Fixes #1831 --------- Signed-off-by: Pratyksh Gupta <pratykshgupta9999@gmail.com> Co-authored-by: salaheldinsoliman <49910731+salaheldinsoliman@users.noreply.github.com>
1 parent 3bee10a commit 5a48c04

5 files changed

Lines changed: 1388 additions & 0 deletions

File tree

src/codegen/encoding/soroban_encoding.rs

Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ pub fn soroban_decode_arg(
151151
Type::Address(_) | Type::String => arg.clone(),
152152

153153
Type::Int(128) | Type::Uint(128) => decode_i128(wrapper_cfg, vartab, arg),
154+
155+
Type::Int(256) | Type::Uint(256) => decode_i256(wrapper_cfg, vartab, arg),
156+
154157
Type::Uint(32) => {
155158
// get payload out of major bits then truncate to 32‑bit
156159
Expression::Trunc {
@@ -570,6 +573,86 @@ pub fn soroban_encode_arg(
570573
expr: encoded,
571574
}
572575
}
576+
Type::Int(256) | Type::Uint(256) => {
577+
// For 256-bit integers, we need to split into four 64-bit pieces
578+
// lo_lo: bits 0-63
579+
// lo_hi: bits 64-127
580+
// hi_lo: bits 128-191
581+
// hi_hi: bits 192-255
582+
583+
let is_signed = matches!(item.ty(), Type::Int(256));
584+
585+
// Extract lo_lo (bits 0-63)
586+
let lo_lo = Expression::Trunc {
587+
loc: Loc::Codegen,
588+
ty: Type::Int(64),
589+
expr: Box::new(item.clone()),
590+
};
591+
592+
// Extract lo_hi (bits 64-127)
593+
let lo_hi_shift = Expression::ShiftRight {
594+
loc: Loc::Codegen,
595+
ty: Type::Int(256),
596+
left: Box::new(item.clone()),
597+
right: Box::new(Expression::NumberLiteral {
598+
loc: Loc::Codegen,
599+
ty: Type::Int(256),
600+
value: BigInt::from(64),
601+
}),
602+
signed: is_signed,
603+
};
604+
605+
let lo_hi = Expression::Trunc {
606+
loc: Loc::Codegen,
607+
ty: Type::Int(64),
608+
expr: Box::new(lo_hi_shift),
609+
};
610+
611+
// Extract hi_lo (bits 128-191)
612+
let hi_lo_shift = Expression::ShiftRight {
613+
loc: Loc::Codegen,
614+
ty: Type::Int(256),
615+
left: Box::new(item.clone()),
616+
right: Box::new(Expression::NumberLiteral {
617+
loc: Loc::Codegen,
618+
ty: Type::Int(256),
619+
value: BigInt::from(128),
620+
}),
621+
signed: is_signed,
622+
};
623+
624+
let hi_lo = Expression::Trunc {
625+
loc: Loc::Codegen,
626+
ty: Type::Int(64),
627+
expr: Box::new(hi_lo_shift),
628+
};
629+
630+
// Extract hi_hi (bits 192-255)
631+
let hi_hi_shift = Expression::ShiftRight {
632+
loc: Loc::Codegen,
633+
ty: Type::Int(256),
634+
left: Box::new(item.clone()),
635+
right: Box::new(Expression::NumberLiteral {
636+
loc: Loc::Codegen,
637+
ty: Type::Int(256),
638+
value: BigInt::from(192),
639+
}),
640+
signed: is_signed,
641+
};
642+
643+
let hi_hi = Expression::Trunc {
644+
loc: Loc::Codegen,
645+
ty: Type::Int(64),
646+
expr: Box::new(hi_hi_shift),
647+
};
648+
649+
let encoded = encode_i256(cfg, vartab, lo_lo, lo_hi, hi_lo, hi_hi, item.ty());
650+
Instr::Set {
651+
loc: item.loc(),
652+
res: obj,
653+
expr: encoded,
654+
}
655+
}
573656
Type::Struct(StructType::UserDefined(n)) => {
574657
let buf = encode_struct(item.clone(), cfg, vartab, ns, n);
575658

@@ -726,6 +809,52 @@ fn encode_i128(
726809
ret
727810
}
728811

812+
/// Encodes a 256-bit integer (signed or unsigned) into a Soroban ScVal.
813+
/// This function handles both Int256 and Uint256 types by splitting them into
814+
/// four 64-bit pieces and using the appropriate host functions.
815+
fn encode_i256(
816+
cfg: &mut ControlFlowGraph,
817+
vartab: &mut Vartable,
818+
lo_lo: Expression,
819+
lo_hi: Expression,
820+
hi_lo: Expression,
821+
hi_hi: Expression,
822+
int256_ty: Type,
823+
) -> Expression {
824+
let ret_var = vartab.temp_anonymous(&lo_lo.ty());
825+
826+
let ret = Expression::Variable {
827+
loc: pt::Loc::Codegen,
828+
ty: lo_lo.ty().clone(),
829+
var_no: ret_var,
830+
};
831+
832+
// For 256-bit integers, we always use the host functions since they can't fit in a 64-bit ScVal
833+
let instr = match int256_ty {
834+
Type::Int(256) => Instr::Call {
835+
res: vec![ret_var],
836+
return_tys: vec![Type::Uint(64)],
837+
call: InternalCallTy::HostFunction {
838+
name: HostFunctions::ObjFromI256Pieces.name().to_string(),
839+
},
840+
args: vec![hi_hi, hi_lo, lo_hi, lo_lo],
841+
},
842+
Type::Uint(256) => Instr::Call {
843+
res: vec![ret_var],
844+
return_tys: vec![Type::Uint(64)],
845+
call: InternalCallTy::HostFunction {
846+
name: HostFunctions::ObjFromU256Pieces.name().to_string(),
847+
},
848+
args: vec![hi_hi, hi_lo, lo_hi, lo_lo],
849+
},
850+
_ => unreachable!(),
851+
};
852+
853+
cfg.add(vartab, instr);
854+
855+
ret
856+
}
857+
729858
fn decode_i128(cfg: &mut ControlFlowGraph, vartab: &mut Vartable, arg: Expression) -> Expression {
730859
let ty = if let Type::Ref(inner_ty) = arg.ty() {
731860
*inner_ty.clone()
@@ -922,6 +1051,248 @@ fn decode_i128(cfg: &mut ControlFlowGraph, vartab: &mut Vartable, arg: Expressio
9221051
ret
9231052
}
9241053

1054+
/// Decodes a 256-bit integer (signed or unsigned) from a Soroban ScVal.
1055+
/// This function handles both Int256 and Uint256 types by retrieving
1056+
/// the four 64-bit pieces from the host object.
1057+
fn decode_i256(cfg: &mut ControlFlowGraph, vartab: &mut Vartable, arg: Expression) -> Expression {
1058+
let ty = if let Type::Ref(inner_ty) = arg.ty() {
1059+
*inner_ty.clone()
1060+
} else {
1061+
arg.ty()
1062+
};
1063+
1064+
let ret_var = vartab.temp_anonymous(&ty);
1065+
1066+
let ret = Expression::Variable {
1067+
loc: pt::Loc::Codegen,
1068+
ty: ty.clone(),
1069+
var_no: ret_var,
1070+
};
1071+
1072+
// For 256-bit integers, we need to extract all four 64-bit pieces
1073+
// lo_lo: bits 0-63
1074+
// lo_hi: bits 64-127
1075+
// hi_lo: bits 128-191
1076+
// hi_hi: bits 192-255
1077+
1078+
// Extract lo_lo (bits 0-63)
1079+
let lo_lo_var_no = vartab.temp_anonymous(&Type::Uint(64));
1080+
let lo_lo_var = Expression::Variable {
1081+
loc: pt::Loc::Codegen,
1082+
ty: Type::Uint(64),
1083+
var_no: lo_lo_var_no,
1084+
};
1085+
1086+
let get_lo_lo_instr = match ty {
1087+
Type::Int(256) => Instr::Call {
1088+
res: vec![lo_lo_var_no],
1089+
return_tys: vec![Type::Uint(64)],
1090+
call: InternalCallTy::HostFunction {
1091+
name: HostFunctions::ObjToI256LoLo.name().to_string(),
1092+
},
1093+
args: vec![arg.clone()],
1094+
},
1095+
Type::Uint(256) => Instr::Call {
1096+
res: vec![lo_lo_var_no],
1097+
return_tys: vec![Type::Uint(64)],
1098+
call: InternalCallTy::HostFunction {
1099+
name: HostFunctions::ObjToU256LoLo.name().to_string(),
1100+
},
1101+
args: vec![arg.clone()],
1102+
},
1103+
_ => unreachable!(),
1104+
};
1105+
1106+
cfg.add(vartab, get_lo_lo_instr);
1107+
1108+
// Extract lo_hi (bits 64-127)
1109+
let lo_hi_var_no = vartab.temp_anonymous(&Type::Uint(64));
1110+
let lo_hi_var = Expression::Variable {
1111+
loc: pt::Loc::Codegen,
1112+
ty: Type::Uint(64),
1113+
var_no: lo_hi_var_no,
1114+
};
1115+
1116+
let get_lo_hi_instr = match ty {
1117+
Type::Int(256) => Instr::Call {
1118+
res: vec![lo_hi_var_no],
1119+
return_tys: vec![Type::Uint(64)],
1120+
call: InternalCallTy::HostFunction {
1121+
name: HostFunctions::ObjToI256LoHi.name().to_string(),
1122+
},
1123+
args: vec![arg.clone()],
1124+
},
1125+
Type::Uint(256) => Instr::Call {
1126+
res: vec![lo_hi_var_no],
1127+
return_tys: vec![Type::Uint(64)],
1128+
call: InternalCallTy::HostFunction {
1129+
name: HostFunctions::ObjToU256LoHi.name().to_string(),
1130+
},
1131+
args: vec![arg.clone()],
1132+
},
1133+
_ => unreachable!(),
1134+
};
1135+
1136+
cfg.add(vartab, get_lo_hi_instr);
1137+
1138+
// Extract hi_lo (bits 128-191)
1139+
let hi_lo_var_no = vartab.temp_anonymous(&Type::Uint(64));
1140+
let hi_lo_var = Expression::Variable {
1141+
loc: pt::Loc::Codegen,
1142+
ty: Type::Uint(64),
1143+
var_no: hi_lo_var_no,
1144+
};
1145+
1146+
let get_hi_lo_instr = match ty {
1147+
Type::Int(256) => Instr::Call {
1148+
res: vec![hi_lo_var_no],
1149+
return_tys: vec![Type::Uint(64)],
1150+
call: InternalCallTy::HostFunction {
1151+
name: HostFunctions::ObjToI256HiLo.name().to_string(),
1152+
},
1153+
args: vec![arg.clone()],
1154+
},
1155+
Type::Uint(256) => Instr::Call {
1156+
res: vec![hi_lo_var_no],
1157+
return_tys: vec![Type::Uint(64)],
1158+
call: InternalCallTy::HostFunction {
1159+
name: HostFunctions::ObjToU256HiLo.name().to_string(),
1160+
},
1161+
args: vec![arg.clone()],
1162+
},
1163+
_ => unreachable!(),
1164+
};
1165+
1166+
cfg.add(vartab, get_hi_lo_instr);
1167+
1168+
// Extract hi_hi (bits 192-255)
1169+
let hi_hi_var_no = vartab.temp_anonymous(&Type::Uint(64));
1170+
let hi_hi_var = Expression::Variable {
1171+
loc: pt::Loc::Codegen,
1172+
ty: Type::Uint(64),
1173+
var_no: hi_hi_var_no,
1174+
};
1175+
1176+
let get_hi_hi_instr = match ty {
1177+
Type::Int(256) => Instr::Call {
1178+
res: vec![hi_hi_var_no],
1179+
return_tys: vec![Type::Uint(64)],
1180+
call: InternalCallTy::HostFunction {
1181+
name: HostFunctions::ObjToI256HiHi.name().to_string(),
1182+
},
1183+
args: vec![arg.clone()],
1184+
},
1185+
Type::Uint(256) => Instr::Call {
1186+
res: vec![hi_hi_var_no],
1187+
return_tys: vec![Type::Uint(64)],
1188+
call: InternalCallTy::HostFunction {
1189+
name: HostFunctions::ObjToU256HiHi.name().to_string(),
1190+
},
1191+
args: vec![arg.clone()],
1192+
},
1193+
_ => unreachable!(),
1194+
};
1195+
1196+
cfg.add(vartab, get_hi_hi_instr);
1197+
1198+
// Now combine all pieces to form the 256-bit value
1199+
// Start with hi_hi (bits 192-255)
1200+
let mut combined = Expression::ZeroExt {
1201+
loc: Loc::Codegen,
1202+
ty: ty.clone(),
1203+
expr: Box::new(hi_hi_var),
1204+
};
1205+
1206+
// Shift left by 64 and add hi_lo (bits 128-191)
1207+
combined = Expression::ShiftLeft {
1208+
loc: Loc::Codegen,
1209+
ty: ty.clone(),
1210+
left: Box::new(combined),
1211+
right: Box::new(Expression::NumberLiteral {
1212+
loc: Loc::Codegen,
1213+
ty: ty.clone(),
1214+
value: BigInt::from(64),
1215+
}),
1216+
};
1217+
1218+
let hi_lo_extended = Expression::ZeroExt {
1219+
loc: Loc::Codegen,
1220+
ty: ty.clone(),
1221+
expr: Box::new(hi_lo_var),
1222+
};
1223+
1224+
combined = Expression::Add {
1225+
loc: Loc::Codegen,
1226+
ty: ty.clone(),
1227+
overflowing: false,
1228+
left: Box::new(combined),
1229+
right: Box::new(hi_lo_extended),
1230+
};
1231+
1232+
// Shift left by 64 and add lo_hi (bits 64-127)
1233+
combined = Expression::ShiftLeft {
1234+
loc: Loc::Codegen,
1235+
ty: ty.clone(),
1236+
left: Box::new(combined),
1237+
right: Box::new(Expression::NumberLiteral {
1238+
loc: Loc::Codegen,
1239+
ty: ty.clone(),
1240+
value: BigInt::from(64),
1241+
}),
1242+
};
1243+
1244+
let lo_hi_extended = Expression::ZeroExt {
1245+
loc: Loc::Codegen,
1246+
ty: ty.clone(),
1247+
expr: Box::new(lo_hi_var),
1248+
};
1249+
1250+
combined = Expression::Add {
1251+
loc: Loc::Codegen,
1252+
ty: ty.clone(),
1253+
overflowing: false,
1254+
left: Box::new(combined),
1255+
right: Box::new(lo_hi_extended),
1256+
};
1257+
1258+
// Shift left by 64 and add lo_lo (bits 0-63)
1259+
combined = Expression::ShiftLeft {
1260+
loc: Loc::Codegen,
1261+
ty: ty.clone(),
1262+
left: Box::new(combined),
1263+
right: Box::new(Expression::NumberLiteral {
1264+
loc: Loc::Codegen,
1265+
ty: ty.clone(),
1266+
value: BigInt::from(64),
1267+
}),
1268+
};
1269+
1270+
let lo_lo_extended = Expression::ZeroExt {
1271+
loc: Loc::Codegen,
1272+
ty: ty.clone(),
1273+
expr: Box::new(lo_lo_var),
1274+
};
1275+
1276+
combined = Expression::Add {
1277+
loc: Loc::Codegen,
1278+
ty: ty.clone(),
1279+
overflowing: false,
1280+
left: Box::new(combined),
1281+
right: Box::new(lo_lo_extended),
1282+
};
1283+
1284+
// Set the final combined value
1285+
let set_instr = Instr::Set {
1286+
loc: pt::Loc::Codegen,
1287+
res: ret_var,
1288+
expr: combined,
1289+
};
1290+
1291+
cfg.add(vartab, set_instr);
1292+
1293+
ret
1294+
}
1295+
9251296
fn extract_tag(arg: Expression) -> Expression {
9261297
let bit_mask = Expression::NumberLiteral {
9271298
loc: pt::Loc::Codegen,

0 commit comments

Comments
 (0)