// ====================================================================
// ConstantInt
// ====================================================================
///|
/// ConstantInt
///
/// Use `Context::getConstInt8, getConstInt16, getConstInt32, getConstInt64`
/// To create a new ConstantInt.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8 = ctx.getConstInt8(0)
/// let i16 = ctx.getConstInt16(1)
/// let i32 = ctx.getConstInt32(-2)
/// let i64 = ctx.getConstInt64(16)
/// inspect(i8, content="i8 0")
/// inspect(i16, content="i16 1")
/// inspect(i32, content="i32 -2")
/// inspect(i64, content="i64 16")
/// }
/// ```
pub struct ConstantInt {
// unique identifier for this value
uid : UInt64
// Value Type
vty : &IntegerType
// the value of this constant
value : Int64
} derive(Eq)
///|
fn ConstantInt::new(vty : &IntegerType, value : Int64) -> ConstantInt {
let value = match vty.asIntegerTypeEnum() {
Int1Type(_) => if value == 0 { 0L } else { 1L }
Int8Type(_) => Int8::from_int64(value).to_int64()
Int16Type(_) => Int16::from_int(value.to_int()).to_int().to_int64()
Int32Type(_) => value.to_int().to_int64()
Int64Type(_) => value
}
let uid = valueUIDAssigner.assign()
ConstantInt::{ uid, vty, value }
}
///|
fn ConstantInt::getValue(self : ConstantInt) -> Int64 {
self.value
}
///|
/// Get the value of this constant as a signed 64-bits integer.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_zero = ctx.getConstInt8(0)
/// let i16_one = ctx.getConstInt16(1)
/// let i32_two = ctx.getConstInt32(2)
/// let i64_m1 = ctx.getConstInt64(-1)
/// let i8_m1 = ctx.getConstInt8(-1)
/// assert_eq(i8_zero.getValueAsInt64(), 0)
/// assert_eq(i16_one.getValueAsInt64(), 1)
/// assert_eq(i32_two.getValueAsInt64(), 2)
/// assert_eq(i64_m1.getValueAsInt64(), -1)
/// assert_eq(i8_m1.getValueAsInt64(), -1)
/// }
/// ```
pub fn ConstantInt::getValueAsInt64(self : ConstantInt) -> Int64 {
self.value
}
///|
/// Get the type of constant int value.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_zero = ctx.getConstInt8(0)
/// let i16_one = ctx.getConstInt16(1)
/// let i32_m2 = ctx.getConstInt32(-2)
/// let i64_m16 = ctx.getConstInt64(-16)
/// inspect(i8_zero.getIntegerType(), content="i8")
/// inspect(i16_one.getIntegerType(), content="i16")
/// inspect(i32_m2.getIntegerType(), content="i32")
/// inspect(i64_m16.getIntegerType(), content="i64")
/// }
/// ```
pub fn ConstantInt::getIntegerType(self : ConstantInt) -> &IntegerType {
self.vty
}
///|
/// Compare the value of this constant int with a signed integer.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// assert_true(ctx.getConstInt8(0).equals(0))
/// assert_true(ctx.getConstInt8(-1).equals(-1))
/// assert_true(ctx.getConstInt8(1).equals(1))
/// assert_true(ctx.getConstInt16(16).equals(16))
/// assert_true(ctx.getConstInt32(-128).equals(-128))
/// }
/// ```
pub fn[T : IntegerNumber] ConstantInt::equals(
self : ConstantInt,
value : T,
) -> Bool {
self.value == value.convert_to_int64()
}
///|
pub fn ConstantInt::isNegative(self : ConstantInt) -> Bool {
self.value < 0
}
///|
pub fn ConstantInt::isMaxValue(self : ConstantInt) -> Bool {
guard self.getType().tryAsIntTypeEnum() is Some(ty)
match ty {
Int1Type(_) => self.value == 1
Int8Type(_) => Int8::from_int64(self.value) == @int8.max_value
Int16Type(_) => Int16::from_int(self.value.to_int()) == @int16.max_value
Int32Type(_) => self.value.to_int() == @int.max_value
Int64Type(_) => self.value == @int64.max_value
}
}
///|
pub fn ConstantInt::isMinValue(self : ConstantInt) -> Bool {
guard self.getType().tryAsIntTypeEnum() is Some(ty)
match ty {
Int1Type(_) => self.value == 0
Int8Type(_) => Int8::from_int64(self.value) == @int8.min_value
Int16Type(_) => Int16::from_int(self.value.to_int()) == @int16.min_value
Int32Type(_) => self.value.to_int() == @int.min_value
Int64Type(_) => self.value == @int64.min_value
}
}
///|
/// Add two ConstantInt, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_0 = ctx.getConstInt8(0)
/// let i8_31 = ctx.getConstInt8(31)
/// inspect(i8_0.add(i8_31), content="i8 31")
/// let i16_5 = ctx.getConstInt16(5)
/// let i16_72 = ctx.getConstInt16(72)
/// inspect(i16_5.add(i16_72), content="i16 77")
/// let i32_m7 = ctx.getConstInt32(-7)
/// let i32_81 = ctx.getConstInt32(81)
/// inspect(i32_m7.add(i32_81), content="i32 74")
/// let i64_16 = ctx.getConstInt64(16)
/// let i64_m33 = ctx.getConstInt64(-33)
/// inspect(i64_16.add(i64_m33), content="i64 -17")
/// assert_true((try? i8_0.add(i16_5)) is Err(_))
/// }
/// ```
pub fn ConstantInt::add(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt addition: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let value = self.value + other.value
let vty = lty
let value = match vty.asIntegerTypeEnum() {
Int1Type(_) => value & 1L
Int8Type(_) => Int8::from_int64(value).to_int64()
Int16Type(_) => Int16::from_int(value.to_int()).to_int64()
Int32Type(_) => value.to_int().to_int64()
Int64Type(_) => value
}
ConstantInt::new(vty, value)
}
///|
/// Subtract two ConstantInt, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_0 = ctx.getConstInt8(0)
/// let i8_31 = ctx.getConstInt8(31)
/// inspect(i8_31.sub(i8_0), content="i8 31")
/// inspect(i8_0.sub(i8_31), content="i8 -31")
/// let i16_5 = ctx.getConstInt16(5)
/// let i16_72 = ctx.getConstInt16(72)
/// inspect(i16_72.sub(i16_5), content="i16 67")
/// let i32_m7 = ctx.getConstInt32(-7)
/// let i32_81 = ctx.getConstInt32(81)
/// inspect(i32_m7.sub(i32_81), content="i32 -88")
/// let i64_16 = ctx.getConstInt64(16)
/// let i64_m33 = ctx.getConstInt64(-33)
/// inspect(i64_16.sub(i64_m33), content="i64 49")
/// assert_true((try? i32_81.sub(i64_16)) is Err(_))
/// }
/// ```
pub fn ConstantInt::sub(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt subtraction: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let value = self.value - other.value
let value = match vty.asIntegerTypeEnum() {
Int1Type(_) => value & 1L
Int8Type(_) => Int8::from_int64(value).to_int64()
Int16Type(_) => Int16::from_int(value.to_int()).to_int64()
Int32Type(_) => value.to_int().to_int64()
Int64Type(_) => value
}
ConstantInt::new(vty, value)
}
///|
/// Multiply two ConstantInt, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_0 = ctx.getConstInt8(0)
/// let i8_31 = ctx.getConstInt8(31)
/// inspect(i8_0.mul(i8_31), content="i8 0")
/// let i16_5 = ctx.getConstInt16(5)
/// let i16_72 = ctx.getConstInt16(72)
/// inspect(i16_5.mul(i16_72), content="i16 360")
/// let i32_m7 = ctx.getConstInt32(-7)
/// let i32_81 = ctx.getConstInt32(81)
/// inspect(i32_m7.mul(i32_81), content="i32 -567")
/// let i64_16 = ctx.getConstInt64(16)
/// let i64_m33 = ctx.getConstInt64(-33)
/// inspect(i64_16.mul(i64_m33), content="i64 -528")
/// }
/// ```
pub fn ConstantInt::mul(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt multiplication: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let value = self.value * other.value
let value = match vty.asIntegerTypeEnum() {
Int1Type(_) => value & 1L
Int8Type(_) => Int8::from_int64(value).to_int64()
Int16Type(_) => Int16::from_int(value.to_int()).to_int64()
Int32Type(_) => value.to_int().to_int64()
Int64Type(_) => value
}
ConstantInt::new(vty, value)
}
///|
/// Divide two ConstantInt, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_1 = ctx.getConstInt8(1)
/// let i8_31 = ctx.getConstInt8(31)
/// inspect(i8_31.sdiv(i8_1), content="i8 31")
/// let i16_5 = ctx.getConstInt16(5)
/// let i16_72 = ctx.getConstInt16(72)
/// inspect(i16_72.sdiv(i16_5), content="i16 14")
/// let i32_m7 = ctx.getConstInt32(-7)
/// let i32_81 = ctx.getConstInt32(81)
/// inspect(i32_m7.sdiv(i32_81), content="i32 0")
/// let i64_16 = ctx.getConstInt64(16)
/// let i64_m33 = ctx.getConstInt64(-33)
/// inspect(i64_16.sdiv(i64_m33), content="i64 0")
/// }
/// ```
pub fn ConstantInt::sdiv(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt signed division: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
guard other.value != 0 else {
let msg = "Division by zero in ConstantInt signed division"
raise LLVMValueError(msg)
}
let vty = lty
let value = self.value / other.value
let value = match vty.asIntegerTypeEnum() {
Int1Type(_) => value & 1L
Int8Type(_) => Int8::from_int64(value).to_int64()
Int16Type(_) => Int16::from_int(value.to_int()).to_int64()
Int32Type(_) => value.to_int().to_int64()
Int64Type(_) => value
}
ConstantInt::new(vty, value)
}
///|
/// Unsigned divide two ConstantInt, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same. It will raise `DivisionByZeroError` if `other` is zero.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
///
/// // Test case 1: i8 unsigned division
/// // 200u8 / 10u8 = 20u8. ConstantInt stores 20L.
/// let i8_200 = ctx.getConstInt8(200) // Internally stored as -56L
/// let i8_10 = ctx.getConstInt8(10)
/// inspect(i8_200.udiv(i8_10), content="i8 20")
///
/// // Test case 2: i8 unsigned division with -1 (255u8)
/// // 255u8 / 2u8 = 127u8. ConstantInt stores 127L.
/// let i8_m1 = ctx.getConstInt8(-1) // Internally stored as -1L, represents 255u8
/// let i8_2 = ctx.getConstInt8(2)
/// inspect(i8_m1.udiv(i8_2), content="i8 127")
///
/// // Test case 3: i32 unsigned division
/// // 2147483647u / 2u = 1073741823u.
/// let i32_max_signed = ctx.getConstInt32(@int.max_value) // 2147483647
/// let i32_2 = ctx.getConstInt32(2)
/// inspect(i32_max_signed.udiv(i32_2), content="i32 1073741823")
///
/// // Test case 4: i64 unsigned division (UInt64.max_value / 2)
/// // (-1L as UInt64) / 2 = (2^64 - 1) / 2 = 2^63 - 1 (which is Int64.max_value)
/// let i64_m1 = ctx.getConstInt64(-1L)
/// let i64_2 = ctx.getConstInt64(2L)
/// inspect(i64_m1.udiv(i64_2), content="i64 9223372036854775807")
///
/// // Test case 5: Type mismatch
/// let i8_5 = ctx.getConstInt8(5)
/// let i16_2 = ctx.getConstInt16(2)
/// assert_true((try? i8_5.udiv(i16_2)) is Err(_))
///
/// // Test case 6: Division by zero
/// let i8_0 = ctx.getConstInt8(0)
/// assert_true((try? i8_5.udiv(i8_0)) is Err(_))
///
/// // Test case 7: i1 unsigned division
/// let i1_true = ctx.getConstTrue()
/// let i1_false = ctx.getConstFalse()
/// inspect(i1_true.udiv(i1_true), content="i1 true") // 1u / 1u = 1u
/// inspect(i1_false.udiv(i1_true), content="i1 false") // 0u / 1u = 0u
/// }
/// ```
pub fn ConstantInt::udiv(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt unsigned division: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
guard other.value != 0L else {
let msg = "Division by zero in ConstantInt unsigned division"
raise LLVMValueError(msg)
}
let vty = lty
let value = match vty.asIntegerTypeEnum() {
Int1Type(_) => self.value / other.value
Int8Type(_) => {
let v1_u8 = self.value.to_byte() // e.g., -56L (representing 200i8) -> 200uy (0xC8)
let v2_u8 = other.value.to_byte() // e.g., 10L (representing 10i8) -> 10uy (0x0A)
let res_u8 = v1_u8 / v2_u8 // 200uy / 10uy = 20uy (0x14)
Int8::from_int64(res_u8.to_int64()).to_int64()
}
Int16Type(_) => {
let v1_u16 = self.value.to_int().to_uint16()
let v2_u16 = other.value.to_int().to_uint16()
let res_u16 = v1_u16 / v2_u16
Int16::from_int(res_u16.to_int()).to_int().to_int64()
}
Int32Type(_) => {
let v1_u32 = self.value.to_int().reinterpret_as_uint()
let v2_u32 = other.value.to_int().reinterpret_as_uint()
let res_u32 = v1_u32 / v2_u32
res_u32.reinterpret_as_int().to_int64()
}
Int64Type(_) =>
(self.value.reinterpret_as_uint64() / other.value.reinterpret_as_uint64()).reinterpret_as_int64()
}
ConstantInt::new(vty, value)
}
///|
/// Bitwise AND two ConstantInt, useful in constant folding.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_6 = ctx.getConstInt8(6) // 00000110
/// let i8_3 = ctx.getConstInt8(3) // 00000011
/// inspect(i8_6.compute_and(i8_3), content="i8 2") // 00000010
/// let i16_m1 = ctx.getConstInt16(-1) // 1111111111111111
/// let i16_all_set = ctx.getConstInt16(-1)
/// inspect(i16_m1.compute_and(i16_all_set), content="i16 -1")
/// let i32_pattern = ctx.getConstInt32(0x0F0F0F0F)
/// let i32_mask = ctx.getConstInt32(0xFF00FF00)
/// inspect(i32_pattern.compute_and(i32_mask), content="i32 251662080") // 0x0F000F00
/// let i64_val = ctx.getConstInt64(0x123456789ABCDEF0L)
/// let i64_low_clear = ctx.getConstInt64(-1L << 4) // ...FFFFFFFFFFFFFFF0
/// inspect(i64_val.compute_and(i64_low_clear), content="i64 1311768467463790320") // 0x123456789ABCDEF0
/// }
/// ```
pub fn ConstantInt::compute_and(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt bitwise AND: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let result_i64 = self.value & other.value
let final_value = match vty.asIntegerTypeEnum() {
Int1Type(_) => result_i64 & 1L
Int8Type(_) => Int8::from_int64(result_i64).to_int64()
Int16Type(_) => Int16::from_int(result_i64.to_int()).to_int64()
Int32Type(_) => result_i64.to_int().to_int64()
Int64Type(_) => result_i64
}
ConstantInt::new(vty, final_value)
}
///|
/// Bitwise OR two ConstantInt, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_6 = ctx.getConstInt8(6) // 00000110
/// let i8_3 = ctx.getConstInt8(3) // 00000011
/// inspect(i8_6.or(i8_3), content="i8 7") // 00000111
/// let i16_1 = ctx.getConstInt16(1) // ...0001
/// let i16_2 = ctx.getConstInt16(2) // ...0010
/// inspect(i16_1.or(i16_2), content="i16 3") // ...0011
/// let i32_pattern = ctx.getConstInt32(0x0F0F0F0F)
/// let i32_mask = ctx.getConstInt32(0xF0F0F0F0)
/// inspect(i32_pattern.or(i32_mask), content="i32 -1") // 0xFFFFFFFF
/// let i64_val = ctx.getConstInt64(0L)
/// let i64_m1 = ctx.getConstInt64(-1L)
/// inspect(i64_val.or(i64_m1), content="i64 -1")
/// }
/// ```
pub fn ConstantInt::or(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt bitwise OR: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let result_i64 = self.value | other.value
let final_value = match vty.asIntegerTypeEnum() {
Int1Type(_) => result_i64 & 1L
Int8Type(_) => Int8::from_int64(result_i64).to_int64()
Int16Type(_) => Int16::from_int(result_i64.to_int()).to_int64()
Int32Type(_) => result_i64.to_int().to_int64()
Int64Type(_) => result_i64
}
ConstantInt::new(vty, final_value)
}
///|
/// Bitwise XOR two ConstantInt, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_6 = ctx.getConstInt8(6) // 00000110
/// let i8_3 = ctx.getConstInt8(3) // 00000011
/// inspect(i8_6.xor(i8_3), content="i8 5") // 00000101
/// let i16_val = ctx.getConstInt16(0xAA) // ...10101010
/// let i16_m1 = ctx.getConstInt16(-1) // ...11111111
/// inspect(i16_val.xor(i16_m1), content="i16 -171") // ...01010101 (which is -0xAB or -171 for i16)
/// let i32_pattern = ctx.getConstInt32(0x0F0F0F0F)
/// let i32_self_xor = i32_pattern.xor(i32_pattern)
/// inspect(i32_self_xor, content="i32 0")
/// let i64_val = ctx.getConstInt64(12345L)
/// let i64_0 = ctx.getConstInt64(0L)
/// inspect(i64_val.xor(i64_0), content="i64 12345")
/// }
/// ```
pub fn ConstantInt::xor(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt bitwise XOR: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let result_i64 = self.value ^ other.value
let final_value = match vty.asIntegerTypeEnum() {
Int1Type(_) => result_i64 & 1L
Int8Type(_) => Int8::from_int64(result_i64).to_int64()
Int16Type(_) => Int16::from_int(result_i64.to_int()).to_int64()
Int32Type(_) => result_i64.to_int().to_int64()
Int64Type(_) => result_i64
}
ConstantInt::new(vty, final_value)
}
///|
/// Left shift (shl) two ConstantInt, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_1 = ctx.getConstInt8(1) // 00000001
/// let i8_3 = ctx.getConstInt8(3) // shift by 3
/// inspect(i8_1.compute_shl(i8_3), content="i8 8") // 00001000
/// let i16_5 = ctx.getConstInt16(5) // ...00000101
/// let i16_2 = ctx.getConstInt16(2) // shift by 2
/// inspect(i16_5.compute_shl(i16_2), content="i16 20") // ...00010100
/// let i32_7 = ctx.getConstInt32(7)
/// let i32_4 = ctx.getConstInt32(4)
/// inspect(i32_7.compute_shl(i32_4), content="i32 112") // 7 << 4 = 112
/// let i64_val = ctx.getConstInt64(0x123L)
/// let i64_8 = ctx.getConstInt64(8L)
/// inspect(i64_val.compute_shl(i64_8), content="i64 74496") // 0x123 << 8 = 0x12300
/// }
/// ```
pub fn ConstantInt::compute_shl(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt left shift: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let result_i64 = self.value << other.value.to_int()
let final_value = match vty.asIntegerTypeEnum() {
Int1Type(_) => result_i64 & 1L
Int8Type(_) => Int8::from_int64(result_i64).to_int64()
Int16Type(_) => Int16::from_int(result_i64.to_int()).to_int64()
Int32Type(_) => result_i64.to_int().to_int64()
Int64Type(_) => result_i64
}
ConstantInt::new(vty, final_value)
}
///|
/// Logical right shift (lshr) two ConstantInt, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_200 = ctx.getConstInt8(200) // 11001000 (as unsigned)
/// let i8_2 = ctx.getConstInt8(2) // shift by 2
/// inspect(i8_200.lshr(i8_2), content="i8 50") // 00110010 (50)
/// let i16_val = ctx.getConstInt16(-32768) // 1000000000000000
/// let i16_1 = ctx.getConstInt16(1) // shift by 1
/// inspect(i16_val.lshr(i16_1), content="i16 16384") // 0100000000000000 (16384)
/// let i32_m1 = ctx.getConstInt32(-1) // 0xFFFFFFFF
/// let i32_4 = ctx.getConstInt32(4) // shift by 4
/// inspect(i32_m1.lshr(i32_4), content="i32 268435455") // 0x0FFFFFFF
/// let i64_val = ctx.getConstInt64(0x8000000000000000L) // MSB set
/// let i64_1 = ctx.getConstInt64(1L)
/// inspect(i64_val.lshr(i64_1), content="i64 4611686018427387904") // logical shift
/// }
/// ```
pub fn ConstantInt::lshr(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt logical right shift: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let value = match vty.asIntegerTypeEnum() {
Int1Type(_) => (self.value >> other.value.to_int()) & 1L
Int8Type(_) => {
let v_u8 = self.value.to_byte()
let shift_amount = other.value.to_int()
let res_u8 = v_u8 >> shift_amount
Int8::from_int64(res_u8.to_int64()).to_int64()
}
Int16Type(_) => {
let v_u16 = self.value.to_int().to_uint16()
let shift_amount = other.value.to_int()
let res_u16 = v_u16 >> shift_amount
Int16::from_int(res_u16.to_int()).to_int().to_int64()
}
Int32Type(_) => {
let v_u32 = self.value.to_int().reinterpret_as_uint()
let shift_amount = other.value.to_int()
let res_u32 = v_u32 >> shift_amount
res_u32.reinterpret_as_int().to_int64()
}
Int64Type(_) => {
let v_u64 = self.value.reinterpret_as_uint64()
let shift_amount = other.value.to_int()
(v_u64 >> shift_amount).reinterpret_as_int64()
}
}
ConstantInt::new(vty, value)
}
///|
/// Arithmetic right shift (ashr) two ConstantInt, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_m1 = ctx.getConstInt8(-1) // 11111111
/// let i8_2 = ctx.getConstInt8(2) // shift by 2
/// inspect(i8_m1.ashr(i8_2), content="i8 -1") // 11111111 (sign extended)
/// let i16_m16 = ctx.getConstInt16(-16) // ...1111111111110000
/// let i16_2 = ctx.getConstInt16(2) // shift by 2
/// inspect(i16_m16.ashr(i16_2), content="i16 -4") // ...1111111111111100
/// let i32_m8 = ctx.getConstInt32(-8)
/// let i32_1 = ctx.getConstInt32(1)
/// inspect(i32_m8.ashr(i32_1), content="i32 -4") // -8 >> 1 = -4 (arithmetic)
/// let i64_m128 = ctx.getConstInt64(-128L)
/// let i64_3 = ctx.getConstInt64(3L)
/// inspect(i64_m128.ashr(i64_3), content="i64 -16") // -128 >> 3 = -16
///
/// // Positive numbers should behave like logical shift
/// let i32_64 = ctx.getConstInt32(64)
/// let i32_2 = ctx.getConstInt32(2)
/// inspect(i32_64.ashr(i32_2), content="i32 16") // 64 >> 2 = 16
/// }
/// ```
pub fn ConstantInt::ashr(
self : ConstantInt,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt arithmetic right shift: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let result_i64 = self.value >> other.value.to_int()
let final_value = match vty.asIntegerTypeEnum() {
Int1Type(_) => result_i64 & 1L
Int8Type(_) => Int8::from_int64(result_i64).to_int64()
Int16Type(_) => Int16::from_int(result_i64.to_int()).to_int64()
Int32Type(_) => result_i64.to_int().to_int64()
Int64Type(_) => result_i64
}
ConstantInt::new(vty, final_value)
}
///|
/// Compare two ConstantInt using the given predicate, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantInt are not the same. Only ICMP_xxx predicates are supported.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_5 = ctx.getConstInt8(5)
/// let i8_10 = ctx.getConstInt8(10)
/// let i8_m5 = ctx.getConstInt8(-5)
///
/// // Test EQ
/// inspect(i8_5.compare(EQ, i8_5), content="i1 true")
/// inspect(i8_5.compare(EQ, i8_10), content="i1 false")
///
/// // Test NE
/// inspect(i8_5.compare(NE, i8_10), content="i1 true")
/// inspect(i8_5.compare(NE, i8_5), content="i1 false")
///
/// // Test SGT (signed greater than)
/// inspect(i8_10.compare(SGT, i8_5), content="i1 true")
/// inspect(i8_5.compare(SGT, i8_10), content="i1 false")
/// inspect(i8_5.compare(SGT, i8_m5), content="i1 true")
///
/// // Test UGT (unsigned greater than)
/// let i8_200 = ctx.getConstInt8(200) // -56 as signed, 200 as unsigned
/// inspect(i8_200.compare(UGT, i8_10), content="i1 true")
/// inspect(i8_10.compare(UGT, i8_200), content="i1 false")
///
/// // Test SLT (signed less than)
/// inspect(i8_5.compare(SLT, i8_10), content="i1 true")
/// inspect(i8_m5.compare(SLT, i8_5), content="i1 true")
///
/// // Test ULE (unsigned less or equal)
/// inspect(i8_5.compare(ULE, i8_10), content="i1 true")
/// inspect(i8_5.compare(ULE, i8_5), content="i1 true")
///
/// // Test type mismatch
/// let i16_5 = ctx.getConstInt16(5)
/// assert_true((try? i8_5.compare(EQ, i16_5)) is Err(_))
/// }
/// ```
pub fn ConstantInt::compare(
self : ConstantInt,
predicate : IntPredicate,
other : ConstantInt,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getIntegerType(), other.getIntegerType())
guard lty == rty else {
let msg = "Type mismatch for ConstantInt comparison: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let ctx = self.getType().getContext()
let result = match predicate {
EQ => self.value == other.value
NE => self.value != other.value
SGT => self.value > other.value
SGE => self.value >= other.value
SLT => self.value < other.value
SLE => self.value <= other.value
UGT =>
match vty.asIntegerTypeEnum() {
Int1Type(_) => self.value > other.value
Int8Type(_) => self.value.to_byte() > other.value.to_byte()
Int16Type(_) =>
self.value.to_int().to_uint16() > other.value.to_int().to_uint16()
Int32Type(_) =>
self.value.to_int().reinterpret_as_uint() >
other.value.to_int().reinterpret_as_uint()
Int64Type(_) =>
self.value.reinterpret_as_uint64() >
other.value.reinterpret_as_uint64()
}
UGE =>
match vty.asIntegerTypeEnum() {
Int1Type(_) => self.value >= other.value
Int8Type(_) => self.value.to_byte() >= other.value.to_byte()
Int16Type(_) =>
self.value.to_int().to_uint16() >= other.value.to_int().to_uint16()
Int32Type(_) =>
self.value.to_int().reinterpret_as_uint() >=
other.value.to_int().reinterpret_as_uint()
Int64Type(_) =>
self.value.reinterpret_as_uint64() >=
other.value.reinterpret_as_uint64()
}
ULT =>
match vty.asIntegerTypeEnum() {
Int1Type(_) => self.value < other.value
Int8Type(_) => self.value.to_byte() < other.value.to_byte()
Int16Type(_) =>
self.value.to_int().to_uint16() < other.value.to_int().to_uint16()
Int32Type(_) =>
self.value.to_int().reinterpret_as_uint() <
other.value.to_int().reinterpret_as_uint()
Int64Type(_) =>
self.value.reinterpret_as_uint64() <
other.value.reinterpret_as_uint64()
}
ULE =>
match vty.asIntegerTypeEnum() {
Int1Type(_) => self.value <= other.value
Int8Type(_) => self.value.to_byte() <= other.value.to_byte()
Int16Type(_) =>
self.value.to_int().to_uint16() <= other.value.to_int().to_uint16()
Int32Type(_) =>
self.value.to_int().reinterpret_as_uint() <=
other.value.to_int().reinterpret_as_uint()
Int64Type(_) =>
self.value.reinterpret_as_uint64() <=
other.value.reinterpret_as_uint64()
}
}
match result {
true => ctx.getConstTrue()
false => ctx.getConstFalse()
}
}
///|
/// Truncate this ConstantInt to a smaller integer type, useful in constant folding.
///
/// **Note**: It will raise `TruncCastInstTypeMismatch` if the source type bit width
/// is not greater than the destination type bit width.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i32_255 = ctx.getConstInt32(255)
/// let i8_255 = i32_255.trunc(ctx.getInt8Ty())
/// inspect(i8_255, content="i8 -1")
/// let i64_0xFFFF = ctx.getConstInt64(0xFFFF)
/// let i16_0xFFFF = i64_0xFFFF.trunc(ctx.getInt16Ty())
/// inspect(i16_0xFFFF, content="i16 -1")
/// let i32_12345 = ctx.getConstInt32(12345)
/// let i16_12345 = i32_12345.trunc(ctx.getInt16Ty())
/// inspect(i16_12345, content="i16 12345")
///
/// // Test error case: cannot truncate to larger type
/// let i8_val = ctx.getConstInt8(42)
/// assert_true((try? i8_val.trunc(ctx.getInt32Ty())) is Err(_))
/// }
/// ```
pub fn ConstantInt::trunc(
self : ConstantInt,
dst_ty : &IntegerType,
) -> ConstantInt raise LLVMValueError {
let src_ty = self.vty.asIntegerTypeEnum()
guard src_ty.getBitWidth() > dst_ty.getBitWidth() else {
let msg = "Truncation cast type mismatch: cannot truncate from \{src_ty.asTypeClass()} (bit width \{src_ty.getBitWidth()}) to \{dst_ty} (bit width \{dst_ty.getBitWidth()})"
raise LLVMValueError(msg)
}
ConstantInt::new(dst_ty, self.value)
}
///|
/// Zero extend this ConstantInt to a larger integer type, useful in constant folding.
///
/// **Note**: It will raise `ZExtCastInstTypeMismatch` if the source type bit width
/// is not less than the destination type bit width.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_255 = ctx.getConstInt8(255) // -1 as signed, 255 as unsigned
/// let i32_255 = i8_255.zext(ctx.getInt32Ty())
/// inspect(i32_255, content="i32 255")
/// let i1_true = ctx.getConstTrue()
/// let i8_true = i1_true.zext(ctx.getInt8Ty())
/// inspect(i8_true, content="i8 1")
/// let i16_42 = ctx.getConstInt16(42)
/// let i64_42 = i16_42.zext(ctx.getInt64Ty())
/// inspect(i64_42, content="i64 42")
///
/// // Test error case: cannot zext to smaller type
/// let i32_val = ctx.getConstInt32(42)
/// assert_true((try? i32_val.zext(ctx.getInt8Ty())) is Err(_))
/// }
/// ```
pub fn ConstantInt::zext(
self : ConstantInt,
dst_ty : &IntegerType,
) -> ConstantInt raise LLVMValueError {
let src_ty = self.vty.asIntegerTypeEnum()
guard src_ty.getBitWidth() < dst_ty.getBitWidth() else {
let msg = "Zero extension cast type mismatch: cannot zero extend from \{src_ty.asTypeClass()} (bit width \{src_ty.getBitWidth()}) to \{dst_ty} (bit width \{dst_ty.getBitWidth()})"
raise LLVMValueError(msg)
}
// Zero extension: treat the value as unsigned
let unsigned_value = match src_ty {
Int1Type(_) => self.value.reinterpret_as_uint64()
Int8Type(_) => self.value.to_byte().to_uint64()
Int16Type(_) => self.value.to_int().to_uint16().to_int().to_uint64()
Int32Type(_) => self.value.to_int().reinterpret_as_uint().to_uint64()
Int64Type(_) => self.value.reinterpret_as_uint64()
}
ConstantInt::new(dst_ty, unsigned_value.reinterpret_as_int64())
}
///|
/// Sign extend this ConstantInt to a larger integer type, useful in constant folding.
///
/// **Note**: It will raise `SExtCastInstTypeMismatch` if the source type bit width
/// is not less than the destination type bit width.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8_m1 = ctx.getConstInt8(-1)
/// let i32_m1 = i8_m1.sext(ctx.getInt32Ty())
/// inspect(i32_m1, content="i32 -1")
/// let i1_true = ctx.getConstTrue()
/// let i8_true = i1_true.sext(ctx.getInt8Ty())
/// inspect(i8_true, content="i8 -1")
/// let i16_42 = ctx.getConstInt16(42)
/// let i64_42 = i16_42.sext(ctx.getInt64Ty())
/// inspect(i64_42, content="i64 42")
///
/// // Test error case: cannot sext to smaller type
/// let i32_val = ctx.getConstInt32(42)
/// assert_true((try? i32_val.sext(ctx.getInt8Ty())) is Err(_))
/// }
/// ```
pub fn ConstantInt::sext(
self : ConstantInt,
dst_ty : &IntegerType,
) -> ConstantInt raise LLVMValueError {
let src_ty = self.vty.asIntegerTypeEnum()
guard src_ty.getBitWidth() < dst_ty.getBitWidth() else {
let msg = "Sign extension cast type mismatch: cannot sign extend from \{src_ty.asTypeClass()} (bit width \{src_ty.getBitWidth()}) to \{dst_ty} (bit width \{dst_ty.getBitWidth()})"
raise LLVMValueError(msg)
}
// Sign extension: need special handling for i1
let extended_value = match src_ty {
Int1Type(_) => if self.value != 0 { -1L } else { 0L }
_ => self.value // Other types are already properly sign-extended in Int64
}
ConstantInt::new(dst_ty, extended_value)
}
///|
/// Convert this ConstantInt to a floating-point type (unsigned interpretation), useful in constant folding.
///
/// **Note**: It will raise `UIToFPInstTypeMismatch` if the source is not an integer type.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i32_255 = ctx.getConstInt32(255)
/// let f32_255 = i32_255.uitofp(ctx.getFloatTy())
/// assert_eq(f32_255.getValue(), 255.0)
/// let i8_m1 = ctx.getConstInt8(-1) // 255 as unsigned
/// let f64_255 = i8_m1.uitofp(ctx.getDoubleTy())
/// assert_eq(f64_255.getValue(), 255.0)
/// let i64_0 = ctx.getConstInt64(0)
/// let f32_0 = i64_0.uitofp(ctx.getFloatTy())
/// assert_eq(f32_0.getValue(), 0.0)
/// }
/// ```
pub fn ConstantInt::uitofp(self : ConstantInt, dst_ty : &FPType) -> ConstantFP {
let src_ty = self.vty.asIntegerTypeEnum()
// Convert unsigned integer to floating point
let unsigned_value = match src_ty {
Int1Type(_) => self.value.reinterpret_as_uint64()
Int8Type(_) => self.value.to_byte().to_uint64()
Int16Type(_) => self.value.to_int().to_uint16().to_int().to_uint64()
Int32Type(_) => self.value.to_int().reinterpret_as_uint().to_uint64()
Int64Type(_) => self.value.reinterpret_as_uint64()
}
let fp_value = unsigned_value.to_double()
ConstantFP::new(dst_ty, fp_value)
}
///|
/// Convert this ConstantInt to a floating-point type (signed interpretation), useful in constant folding.
///
/// **Note**: It will raise `SIToFPInstTypeMismatch` if the source is not an integer type.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i32_m42 = ctx.getConstInt32(-42)
/// let f32_m42 = i32_m42.sitofp(ctx.getFloatTy())
/// assert_eq(f32_m42.getValue(), -42.0)
/// let i8_127 = ctx.getConstInt8(127)
/// let f64_127 = i8_127.sitofp(ctx.getDoubleTy())
/// assert_eq(f64_127.getValue(), 127.0)
/// let i64_0 = ctx.getConstInt64(0)
/// let f32_0 = i64_0.sitofp(ctx.getFloatTy())
/// assert_eq(f32_0.getValue(), 0.0)
/// }
/// ```
pub fn ConstantInt::sitofp(self : ConstantInt, dst_ty : &FPType) -> ConstantFP {
// Convert signed integer to floating point
let fp_value = self.value.to_double()
ConstantFP::new(dst_ty, fp_value)
}
///|
/// Convert this ConstantInt to a pointer type, useful in constant folding.
///
/// **Note**: It will raise `IntToPtrCastInstTypeMismatch` if the source is not an integer type.
/// For constant folding, this always returns a null pointer since we cannot create
/// meaningful pointer constants from integer values at compile time.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i64_0 = ctx.getConstInt64(0)
/// let null_ptr = i64_0.inttoptr()
/// inspect(null_ptr, content="ptr null")
/// let i32_42 = ctx.getConstInt32(42)
/// let ptr_42 = i32_42.inttoptr()
/// inspect(ptr_42, content="ptr null") // Still null in constant folding
/// }
/// ```
pub fn ConstantInt::inttoptr(self : ConstantInt) -> ConstantPointerNull {
let ctx = self.getType().getContext()
ConstantPointerNull::new(ctx)
}
///|
/// Bitcast this ConstantInt to another primitive type of the same bit width, useful in constant folding.
///
/// **Note**: It will raise `BitCastInstTypeMismatch` if the types have different bit widths
/// or if the cast is invalid. It will raise `BitCastOnlyAcceptPrimitiveTypes` if the source
/// is not a primitive type.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
///
/// // Test i32 to f32 bitcast
/// let i32_val = ctx.getConstInt32(0x40000000) // 2.0 in float
/// let f32_val = i32_val.bitcast(ctx.getFloatTy())
/// guard f32_val.asConstantEnum() is ConstantFP(f32_val)
/// assert_eq(f32_val.getValue(), 2.0)
///
/// // Test i64 to f64 bitcast
/// let i64_val = ctx.getConstInt64(0x4000000000000000L) // 2.0 in double
/// let f64_val = i64_val.bitcast(ctx.getDoubleTy())
/// guard f64_val.asConstantEnum() is ConstantFP(f64_val)
/// assert_eq(f64_val.getValue(), 2.0)
///
/// // Test error case: different bit widths
/// let i8_val = ctx.getConstInt8(42)
/// assert_true((try? i8_val.bitcast(ctx.getFloatTy())) is Err(_))
/// }
/// ```
pub fn ConstantInt::bitcast(
self : ConstantInt,
dst_ty : &PrimitiveType,
) -> &Constant raise LLVMValueError {
let src_ty = self.getType()
guard src_ty != dst_ty else {
let msg = "Bitcast type mismatch: source type \{src_ty} is the same as destination type \{dst_ty}"
raise LLVMValueError(msg)
}
guard src_ty.tryAsPrimitiveTypeEnum() is Some(src_ty_prim) else {
let msg = "Bitcast only accepts primitive types: source type \{src_ty} is not a primitive type"
raise LLVMValueError(msg)
}
guard src_ty_prim.getBitWidth() == dst_ty.getBitWidth() else {
let msg = "Bitcast type mismatch: source type \{src_ty} (bit width \{src_ty_prim.getBitWidth()}) and destination type \{dst_ty} (bit width \{dst_ty.getBitWidth()}) have different bit widths"
raise LLVMValueError(msg)
}
match dst_ty.asPrimitiveTypeEnum() {
// Integer to integer
Int1Type(_) | Int8Type(_) | Int16Type(_) | Int32Type(_) | Int64Type(_) =>
ConstantInt::new(dst_ty.tryAsIntType().unwrap(), self.value) as &Constant
// Integer to floating point
FloatType(dst_ty) => {
// For i32 to f32, we need to treat the 32-bit value as float bits
let i32_bits = self.value.to_int().reinterpret_as_uint()
let f32_value = Float::reinterpret_from_uint(i32_bits).to_double()
ConstantFP::new(dst_ty, f32_value)
}
DoubleType(dst_ty) => {
let fp_bits = self.value.reinterpret_as_uint64()
let fp_value = fp_bits.reinterpret_as_double()
ConstantFP::new(dst_ty, fp_value)
}
HalfType(_) | BFloatType(_) | FP128Type(_) => {
// For other FP types, we also interpret as double for now
let fp_bits = self.value.reinterpret_as_uint64()
let fp_value = fp_bits.reinterpret_as_double()
ConstantFP::new(dst_ty.tryAsFPType().unwrap(), fp_value)
}
}
}
///|
pub impl Value for ConstantInt with getValueBase(self) {
ValueBase::{ uid: self.uid, vty: self.vty, users: [] }
}
///|
pub impl Value for ConstantInt with asValueEnum(self) {
ConstantInt(self)
}
///|
pub impl Value for ConstantInt with getValueRepr(self) {
let vty = self.getIntegerType()
let val = match vty.asIntegerTypeEnum() {
Int1Type(_) => if self.value != 0 { "true" } else { "false" }
_ => self.value.to_string()
}
"\{val}"
}
///|
pub impl Value for ConstantInt with getName(_) {
None
}
///|
pub impl Value for ConstantInt with setName(_, _) {
let msg = "Calling always failed function `ConstantInt::setName`. " +
"Set name for ConstantInt is not allowed."
raise LLVMValueError(msg)
}
///|
pub impl Value for ConstantInt with removeName(_) {
()
}
///|
pub impl Value for ConstantInt with getNameOrSlot(_) {
None
}
///|
pub impl Constant for ConstantInt with asConstantEnum(self) {
ConstantInt(self)
}
///|
pub impl Show for ConstantInt with output(self, logger) {
let val_str = self.getValueRepr()
logger.write_string("\{self.getIntegerType()} \{val_str}")
}
// ====================================================================
// ConstantFP
// ====================================================================
///|
pub struct ConstantFP {
// Unique identifier
uid : UInt64
// Value type
vty : &FPType
// Value stored in this constant
value : Double
} derive(Eq)
///|
fn ConstantFP::new(vty : &FPType, value : Double) -> ConstantFP {
let uid = valueUIDAssigner.assign()
ConstantFP::{ uid, vty, value }
}
///|
pub fn ConstantFP::getFPType(self : ConstantFP) -> &FPType {
self.vty
}
///|
pub fn ConstantFP::getValue(self : ConstantFP) -> Double {
self.value
}
///|
pub fn[T : Floating] ConstantFP::equals(self : ConstantFP, value : T) -> Bool {
self.value == value.to_float64()
}
///|
pub fn[T : Floating] ConstantFP::exactlyEquals(
self : ConstantFP,
value : T,
) -> Bool {
self.value.reinterpret_as_uint64() ==
value.to_float64().reinterpret_as_uint64()
}
///|
/// Add two ConstantFP, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantFP are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let f32_0 = ctx.getConstFloat(0.0)
/// let f32_1_5 = ctx.getConstFloat(1.5)
/// let result1 = f32_0.add(f32_1_5)
/// assert_eq(result1.getValue(), 1.5)
/// let f64_2_5 = ctx.getConstDouble(2.5)
/// let f64_m1_5 = ctx.getConstDouble(-1.5)
/// let result2 = f64_2_5.add(f64_m1_5)
/// assert_eq(result2.getValue(), 1.0)
/// }
/// ```
pub fn ConstantFP::add(
self : ConstantFP,
other : ConstantFP,
) -> ConstantFP raise LLVMValueError {
let (lty, rty) = (self.getFPType(), other.getFPType())
guard lty == rty else {
let msg = "Type mismatch for ConstantFP addition: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let value = self.value + other.value
ConstantFP::new(vty, value)
}
///|
/// Subtract two ConstantFP, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantFP are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let f32_0 = ctx.getConstFloat(0.0)
/// let f32_1_5 = ctx.getConstFloat(1.5)
/// let result1 = f32_0.sub(f32_1_5)
/// assert_eq(result1.getValue(), -1.5)
/// let f64_2_5 = ctx.getConstDouble(2.5)
/// let f64_m1_5 = ctx.getConstDouble(-1.5)
/// let result2 = f64_2_5.sub(f64_m1_5)
/// assert_eq(result2.getValue(), 4.0)
/// }
/// ```
pub fn ConstantFP::sub(
self : ConstantFP,
other : ConstantFP,
) -> ConstantFP raise LLVMValueError {
let (lty, rty) = (self.getFPType(), other.getFPType())
guard lty == rty else {
let msg = "Type mismatch for ConstantFP subtraction: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let value = self.value - other.value
ConstantFP::new(vty, value)
}
///|
/// Multiply two ConstantFP, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantFP are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let f32_0 = ctx.getConstFloat(0.0)
/// let f32_1_5 = ctx.getConstFloat(1.5)
/// let result1 = f32_0.mul(f32_1_5)
/// assert_eq(result1.getValue(), 0.0)
/// let f64_2_5 = ctx.getConstDouble(2.5)
/// let f64_m1_5 = ctx.getConstDouble(-1.5)
/// let result2 = f64_2_5.mul(f64_m1_5)
/// assert_eq(result2.getValue(), -3.75)
/// }
/// ```
pub fn ConstantFP::mul(
self : ConstantFP,
other : ConstantFP,
) -> ConstantFP raise LLVMValueError {
let (lty, rty) = (self.getFPType(), other.getFPType())
guard lty == rty else {
let msg = "Type mismatch for ConstantFP multiplication: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let value = self.value * other.value
ConstantFP::new(vty, value)
}
///|
/// Divide two ConstantFP, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForBinaryOp` if the types of two
/// ConstantFP are not the same.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let f32_0 = ctx.getConstFloat(0.0)
/// let f32_1_5 = ctx.getConstFloat(1.5)
/// let result1 = f32_0.div(f32_1_5)
/// assert_eq(result1.getValue(), 0.0)
/// let f64_2_5 = ctx.getConstDouble(2.5)
/// let f64_m1_5 = ctx.getConstDouble(-1.5)
/// let result2 = f64_2_5.div(f64_m1_5)
/// assert_eq(result2.getValue(), -5.0 / 3.0)
/// }
/// ```
pub fn ConstantFP::div(
self : ConstantFP,
other : ConstantFP,
) -> ConstantFP raise LLVMValueError {
let (lty, rty) = (self.getFPType(), other.getFPType())
guard lty == rty else {
let msg = "Type mismatch for ConstantFP division: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let vty = lty
let value = self.value / other.value
ConstantFP::new(vty, value)
}
///|
/// Compare two ConstantFP using the given predicate, useful in constant folding.
///
/// **Note**: It will raise `TypeMismatchForCmpInst` if the types of two
/// ConstantFP are not the same. Only FCMP_xxx predicates are supported.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let f32_1_5 = ctx.getConstFloat(1.5)
/// let f32_2_5 = ctx.getConstFloat(2.5)
/// let f32_nan = ctx.getConstFloat(0.0 / 0.0)
///
/// // Test FCMP_OEQ (ordered equal)
/// inspect(f32_1_5.compare(OEQ, f32_1_5), content="i1 true")
/// inspect(f32_1_5.compare(OEQ, f32_2_5), content="i1 false")
/// inspect(f32_nan.compare(OEQ, f32_nan), content="i1 false") // NaN != NaN
///
/// // Test UEQ (unordered equal)
/// inspect(f32_1_5.compare(UEQ, f32_1_5), content="i1 true")
/// inspect(f32_nan.compare(UEQ, f32_nan), content="i1 true") // NaN == NaN in unordered
///
/// // Test OGT (ordered greater than)
/// inspect(f32_2_5.compare(OGT, f32_1_5), content="i1 true")
/// inspect(f32_1_5.compare(OGT, f32_2_5), content="i1 false")
/// inspect(f32_nan.compare(OGT, f32_1_5), content="i1 false") // NaN comparisons are false
///
/// // Test UGT (unordered greater than)
/// inspect(f32_2_5.compare(UGT, f32_1_5), content="i1 true")
/// inspect(f32_nan.compare(UGT, f32_1_5), content="i1 true") // NaN makes it unordered
///
/// // Test OLT (ordered less than)
/// inspect(f32_1_5.compare(OLT, f32_2_5), content="i1 true")
/// inspect(f32_2_5.compare(OLT, f32_1_5), content="i1 false")
///
/// // Test ORD (ordered - no NaNs)
/// inspect(f32_1_5.compare(ORD, f32_2_5), content="i1 true")
/// inspect(f32_nan.compare(ORD, f32_1_5), content="i1 false")
///
/// // Test UNO (unordered - has NaNs)
/// inspect(f32_1_5.compare(UNO, f32_2_5), content="i1 false")
/// inspect(f32_nan.compare(UNO, f32_1_5), content="i1 true")
///
/// // Test TRUE and FCMP_FALSE
/// inspect(f32_1_5.compare(TRUE, f32_2_5), content="i1 true")
/// inspect(f32_1_5.compare(FALSE, f32_2_5), content="i1 false")
///
/// // Test type mismatch
/// let f64_1_5 = ctx.getConstDouble(1.5)
/// assert_true((try? f32_1_5.compare(OEQ, f64_1_5)) is Err(_))
/// }
/// ```
pub fn ConstantFP::compare(
self : ConstantFP,
predicate : FloatPredicate,
other : ConstantFP,
) -> ConstantInt raise LLVMValueError {
let (lty, rty) = (self.getFPType(), other.getFPType())
guard lty == rty else {
let msg = "Type mismatch for ConstantFP comparison: \{lty} vs \{rty}"
raise LLVMValueError(msg)
}
let self_val = self.value
let other_val = other.value
let self_is_nan = self_val.is_nan()
let other_is_nan = other_val.is_nan()
let is_unordered = self_is_nan || other_is_nan
let ctx = self.getType().getContext()
let result = match predicate {
FALSE => false
TRUE => true
OEQ => not(is_unordered) && self_val == other_val
OGT => not(is_unordered) && self_val > other_val
OGE => not(is_unordered) && self_val >= other_val
OLT => not(is_unordered) && self_val < other_val
OLE => not(is_unordered) && self_val <= other_val
ONE => not(is_unordered) && self_val != other_val
ORD => not(is_unordered)
UNO => is_unordered
UEQ => is_unordered || self_val == other_val
UGT => is_unordered || self_val > other_val
UGE => is_unordered || self_val >= other_val
ULT => is_unordered || self_val < other_val
ULE => is_unordered || self_val <= other_val
UNE => is_unordered || self_val != other_val
}
match result {
true => ctx.getConstTrue()
false => ctx.getConstFalse()
}
}
///|
/// Truncate this ConstantFP to a smaller floating-point type, useful in constant folding.
///
/// **Note**: It will raise `FPTruncCastInstTypeMismatch` if the source type bit width
/// is not greater than the destination type bit width.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
///
/// // Test f64 to f32 truncation
/// let f64_1_5 = ctx.getConstDouble(1.5)
/// let f32_1_5 = f64_1_5.fptrunc(ctx.getFloatTy())
/// assert_eq(f32_1_5.getValue(), 1.5)
///
/// // Test f64 to f32 truncation with another value
/// let f64_precise = ctx.getConstDouble(2.5)
/// let f32_truncated = f64_precise.fptrunc(ctx.getFloatTy())
/// // In constant folding, the value is preserved
/// assert_eq(f32_truncated.getValue(), 2.5)
///
/// // Test error case: cannot truncate to larger type
/// let f32_val = ctx.getConstFloat(2.0)
/// assert_true((try? f32_val.fptrunc(ctx.getDoubleTy())) is Err(_))
/// }
/// ```
pub fn ConstantFP::fptrunc(
self : ConstantFP,
dst_ty : &FPType,
) -> ConstantFP raise LLVMValueError {
let src_ty = self.vty
guard src_ty.getBitWidth() > dst_ty.getBitWidth() else {
let msg = "Floating-point truncation cast type mismatch: cannot truncate from \{src_ty} (bit width \{src_ty.getBitWidth()}) to \{dst_ty} (bit width \{dst_ty.getBitWidth()})"
raise LLVMValueError(msg)
}
// For truncation, we simply use the current value (precision loss handled by Double)
let value = Float::from_double(self.value).to_double()
ConstantFP::new(dst_ty, value)
}
///|
/// Extend this ConstantFP to a larger floating-point type, useful in constant folding.
///
/// **Note**: It will raise `FPExtCastInstTypeMismatch` if the source type bit width
/// is not less than the destination type bit width.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
///
/// // Test f32 to f64 extension
/// let f32_1_5 = ctx.getConstFloat(1.5)
/// let f64_1_5 = f32_1_5.fpext(ctx.getDoubleTy())
/// assert_eq(f64_1_5.getValue(), 1.5)
///
/// // Test f32 to f64 extension preserves value
/// let f32_pi = ctx.getConstFloat(3.14159265)
/// let f64_pi = f32_pi.fpext(ctx.getDoubleTy())
/// assert_eq(f64_pi.getValue(), 3.1415927410125732)
///
/// // Test error case: cannot extend to smaller type
/// let f64_val = ctx.getConstDouble(2.0)
/// assert_true((try? f64_val.fpext(ctx.getFloatTy())) is Err(_))
/// }
/// ```
pub fn ConstantFP::fpext(
self : ConstantFP,
dst_ty : &FPType,
) -> ConstantFP raise LLVMValueError {
let src_ty = self.vty
guard src_ty.getBitWidth() < dst_ty.getBitWidth() else {
let msg = "Floating-point extension cast type mismatch: cannot extend from \{src_ty} (bit width \{src_ty.getBitWidth()}) to \{dst_ty} (bit width \{dst_ty.getBitWidth()})"
raise LLVMValueError(msg)
}
// For extension, we simply use the current value (no precision loss)
ConstantFP::new(dst_ty, self.value)
}
///|
/// Convert this ConstantFP to an unsigned integer type, useful in constant folding.
///
/// **Note**: It will raise `FPToUIInstTypeMismatch` if the source is not a floating-point type.
/// The conversion truncates towards zero (similar to C cast).
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
///
/// // Test positive float to unsigned integer
/// let f32_3_7 = ctx.getConstFloat(3.7)
/// let i32_3 = f32_3_7.fptoui(ctx.getInt32Ty())
/// assert_eq(i32_3.getValueAsInt64(), 3)
///
/// // Test zero conversion
/// let f64_0 = ctx.getConstDouble(0.0)
/// let i64_0 = f64_0.fptoui(ctx.getInt64Ty())
/// assert_eq(i64_0.getValueAsInt64(), 0)
///
/// // Test large float to i8 (overflow behavior)
/// let f32_1000 = ctx.getConstFloat(1000.0)
/// let i8_overflow = f32_1000.fptoui(ctx.getInt8Ty())
/// // The value will be truncated to fit in i8 range (1000 in i8 is -24)
/// assert_eq(i8_overflow.getValueAsInt64(), -24)
///
/// // Test fractional part truncation
/// let f64_9_9 = ctx.getConstDouble(9.9)
/// let i32_9 = f64_9_9.fptoui(ctx.getInt32Ty())
/// assert_eq(i32_9.getValueAsInt64(), 9)
/// }
/// ```
pub fn ConstantFP::fptoui(
self : ConstantFP,
dst_ty : &IntegerType,
) -> ConstantInt {
// Convert float to unsigned integer (truncate towards zero)
let int_value = if self.value < 0.0 {
0L // Negative values become 0 in unsigned conversion
} else {
self.value.to_int64()
}
ConstantInt::new(dst_ty, int_value)
}
///|
/// Convert this ConstantFP to a signed integer type, useful in constant folding.
///
/// **Note**: It will raise `FPToSIInstTypeMismatch` if the source is not a floating-point type.
/// The conversion truncates towards zero (similar to C cast).
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
///
/// // Test positive float to signed integer
/// let f32_3_7 = ctx.getConstFloat(3.7)
/// let i32_3 = f32_3_7.fptosi(ctx.getInt32Ty())
/// assert_eq(i32_3.getValueAsInt64(), 3)
///
/// // Test negative float to signed integer
/// let f64_m5_2 = ctx.getConstDouble(-5.2)
/// let i64_m5 = f64_m5_2.fptosi(ctx.getInt64Ty())
/// assert_eq(i64_m5.getValueAsInt64(), -5)
///
/// // Test zero conversion
/// let f32_0 = ctx.getConstFloat(0.0)
/// let i16_0 = f32_0.fptosi(ctx.getInt16Ty())
/// assert_eq(i16_0.getValueAsInt64(), 0)
///
/// // Test fractional part truncation
/// let f64_m9_9 = ctx.getConstDouble(-9.9)
/// let i32_m9 = f64_m9_9.fptosi(ctx.getInt32Ty())
/// assert_eq(i32_m9.getValueAsInt64(), -9)
/// }
/// ```
pub fn ConstantFP::fptosi(
self : ConstantFP,
dst_ty : &IntegerType,
) -> ConstantInt {
// Convert float to signed integer (truncate towards zero)
let int_value = self.value.to_int64()
ConstantInt::new(dst_ty, int_value)
}
///|
/// Bitcast this ConstantFP to another primitive type of the same bit width, useful in constant folding.
///
/// **Note**: It will raise `BitCastInstTypeMismatch` if the types have different bit widths
/// or if the cast is invalid. It will raise `BitCastOnlyAcceptPrimitiveTypes` if the source
/// is not a primitive type.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
///
/// // Test f32 to i32 bitcast
/// let f32_2_0 = ctx.getConstFloat(2.0)
/// let i32_bits = f32_2_0.bitcast(ctx.getInt32Ty())
/// guard i32_bits.asConstantEnum() is ConstantInt(i32_bits)
/// assert_eq(i32_bits.getValueAsInt64(), 0x40000000) // 2.0f bit pattern
///
/// // Test f64 to i64 bitcast
/// let f64_2_0 = ctx.getConstDouble(2.0)
/// let i64_bits = f64_2_0.bitcast(ctx.getInt64Ty())
/// guard i64_bits.asConstantEnum() is ConstantInt(i64_bits)
/// assert_eq(i64_bits.getValueAsInt64(), 0x4000000000000000L) // 2.0 bit pattern
///
/// // Test f32 to f32 (same type should error)
/// let f32_val = ctx.getConstFloat(1.0)
/// assert_true((try? f32_val.bitcast(ctx.getFloatTy())) is Err(_))
///
/// // Test error case: different bit widths
/// let f64_val = ctx.getConstDouble(42.0)
/// assert_true((try? f64_val.bitcast(ctx.getInt32Ty())) is Err(_))
/// }
/// ```
pub fn ConstantFP::bitcast(
self : ConstantFP,
dst_ty : &PrimitiveType,
) -> &Constant raise LLVMValueError {
let src_ty = self.getType()
guard src_ty != dst_ty else {
let msg = "Bitcast type mismatch: source type \{src_ty} is the same as destination type \{dst_ty}"
raise LLVMValueError(msg)
}
guard src_ty.tryAsPrimitiveTypeEnum() is Some(src_ty_prim) else {
let msg = "Bitcast only accepts primitive types: source type \{src_ty} is not a primitive type"
raise LLVMValueError(msg)
}
guard src_ty_prim.getBitWidth() == dst_ty.getBitWidth() else {
let msg = "Bitcast type mismatch: source type \{src_ty} (bit width \{src_ty_prim.getBitWidth()}) and destination type \{dst_ty} (bit width \{dst_ty.getBitWidth()}) have different bit widths"
raise LLVMValueError(msg)
}
match dst_ty.asPrimitiveTypeEnum() {
// Floating point to integer
Int32Type(dst_ty) => {
// For f32 to i32, reinterpret the 32-bit float as 32-bit int
let f32_bits = Float::from_double(self.value).reinterpret_as_uint()
let i32_value = f32_bits.reinterpret_as_int().to_int64()
ConstantInt::new(dst_ty, i32_value) as &Constant
}
Int64Type(dst_ty) => {
// For f64 to i64, reinterpret the 64-bit double as 64-bit int
let f64_bits = self.value.reinterpret_as_uint64()
let i64_value = f64_bits.reinterpret_as_int64()
ConstantInt::new(dst_ty, i64_value) as &Constant
}
// Floating point to floating point
FloatType(_) | DoubleType(_) | HalfType(_) | BFloatType(_) | FP128Type(_) =>
ConstantFP::new(dst_ty.tryAsFPType().unwrap(), self.value) as &Constant
// Other integer types
Int1Type(_) | Int8Type(_) | Int16Type(_) => {
// For other integer types, we need proper bit width matching
let msg = "Bitcast type mismatch: source type \{src_ty} and destination type \{dst_ty} have incompatible types for bitcast"
raise LLVMValueError(msg)
}
}
}
///|
pub impl Value for ConstantFP with getValueBase(self) {
ValueBase::{ uid: self.uid, vty: self.vty, users: [] }
}
///|
pub impl Value for ConstantFP with getValueRepr(self) {
let val_str = self.value
.reinterpret_as_uint64()
.to_string(radix=16)
.to_upper()
"0x\{val_str}"
}
///|
pub impl Value for ConstantFP with asValueEnum(self) {
ConstantFP(self)
}
///|
pub impl Value for ConstantFP with getName(_) {
None
}
///|
pub impl Value for ConstantFP with setName(_, _) {
let msg = "Calling always failed function `ConstantFP::setName`. " +
"Set name for ConstantFP is not allowed."
raise LLVMValueError(msg)
}
///|
pub impl Value for ConstantFP with removeName(_) {
()
}
///|
pub impl Value for ConstantFP with getNameOrSlot(_) {
None
}
///|
pub impl Constant for ConstantFP with asConstantEnum(self) {
ConstantFP(self)
}
///|
pub impl Show for ConstantFP with output(self, logger) {
let val_str = self.getValueRepr()
logger.write_string("\{self.getFPType()} \{val_str}")
}
// ====================================================================
// ConstantAggregateZero
// ====================================================================
//pub struct ConstantAggregateZero {
// base : ValueBase
//} derive(Eq, Hash)
//
//fn ConstantAggregateZero::new(vty : &Type) -> ConstantAggregateZero {
// guard vty.asTypeEnum() is (
// ArrayType(_)
// | StructType(_)
// | VectorType(_)
// | ScalableVectorType(_)) else {
// llvm_unreachable("ConstantAggregateZero::new: vty is not ArrayType, VectorType or StructType")
// }
// let base = ValueBase::{ vty, users: [] }
// ConstantAggregateZero::{ base }
//}
//
//impl Value for ConstantAggregateZero with getValueBase(self) {
// self.base
//}
//
//impl Value for ConstantAggregateZero with asValueEnum(self) {
// ConstantAggregateZero(self)
//}
//
//impl Show for ConstantAggregateZero with output(self, logger) {
// logger.write_string("\{self.getType()} zeroinitializer")
//}
// ====================================================================
// ConstantPointerNull
// ====================================================================
///|
pub struct ConstantPointerNull {
// Unique identifier
uid : UInt64
// Value type, must be a pointer type
vty : PointerType
} derive(Eq)
///|
fn ConstantPointerNull::new(
ctx : Context,
addressSpace? : UInt = 0,
) -> ConstantPointerNull {
let vty = PointerType::new(ctx, addressSpace=AddressSpace(addressSpace))
let uid = valueUIDAssigner.assign()
ConstantPointerNull::{ uid, vty }
}
///|
pub impl Value for ConstantPointerNull with getValueBase(self) {
ValueBase::{ uid: self.uid, vty: self.vty, users: [] }
}
///|
pub impl Value for ConstantPointerNull with asValueEnum(self) {
ConstantPointerNull(self)
}
///|
pub impl Value for ConstantPointerNull with getValueRepr(_) {
"null"
}
///|
pub impl Value for ConstantPointerNull with getName(_) {
None
}
///|
pub impl Value for ConstantPointerNull with setName(_, _) {
let msg = "Calling always failed function `ConstantPointerNull::setName`. " +
"Set name for ConstantPointerNull is not allowed."
raise LLVMValueError(msg)
}
///|
pub impl Value for ConstantPointerNull with removeName(_) {
()
}
///|
pub impl Value for ConstantPointerNull with getNameOrSlot(_) {
None
}
///|
pub impl Constant for ConstantPointerNull with asConstantEnum(self) {
ConstantPointerNull(self)
}
///|
pub impl Show for ConstantPointerNull with output(self, logger) {
logger.write_string("\{self.getType()} null")
}
// ====================================================================
// ConstantArray
// ====================================================================
///|
pub struct ConstantArray {
// Unique identifier
uid : UInt64
// Value type, must be an array type
vty : ArrayType
data : Either[Array[&Constant], NumberArrayEnum]
} derive(Eq)
///|
//fn ConstantArray::newConstantArray(
// vty : &Type,
// data : Array[&Constant]
//) -> ConstantArray {
// guard vty.asTypeEnum() is ArrayType(arrTy) else {
// llvm_unreachable("ConstantArray::new: vty is not ArrayType")
// }
// let eleCnt = arrTy.getElementCount().reinterpret_as_int()
// guard data.length() == eleCnt else {
// llvm_unreachable(
// "ConstantArray::new: data length is not equal to element count",
// )
// }
// let eleTy = arrTy.getElementType()
// let consistency = data
// .map(d => d.getType())
// .fold(init=true, fn(acc, ty) { acc && ty == eleTy })
// guard consistency else {
// llvm_unreachable(
// "ConstantArray::new: data type is not consistent with elementType",
// )
// }
// let base = ValueBase::new(vty)
// ConstantArray::{ base, data: Either::Left(data) }
//}
// TODO: use `Int8` instead of `Int` when standard library support `Int8`
///|
fn ConstantArray::newInt8Array(
vty : ArrayType,
data : Array[Int],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int8Type(_) else {
llvm_unreachable("ConstantArray::new: elementType is not Int8Type")
}
let data = data.map(i => Int8::from(i))
|> Int8Array::from
|> NumberArrayEnum::Int8Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data }
}
///|
fn ConstantArray::newInt16Array(
vty : ArrayType,
data : Array[Int16],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int16Type(_) else {
llvm_unreachable("ConstantArray::new: elementType is not Int16Type")
}
let data = data
|> Int16Array::from
|> NumberArrayEnum::Int16Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data }
}
///|
fn ConstantArray::newInt32Array(
vty : ArrayType,
data : Array[Int],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int32Type(_) else {
llvm_unreachable("ConstantArray::new: elementType is not Int32Type")
}
let data = data
|> Int32Array::from
|> NumberArrayEnum::Int32Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data }
}
///|
fn ConstantArray::newInt64Array(
vty : ArrayType,
data : Array[Int64],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int64Type(_) else {
llvm_unreachable("ConstantArray::new: elementType is not Int64Type")
}
let data = data
|> Int64Array::from
|> NumberArrayEnum::Int64Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data }
}
///|
fn ConstantArray::newUInt8Array(
vty : ArrayType,
data : Array[Byte],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int8Type(_) else {
llvm_unreachable("ConstantArray::new: elementType is not Int8Type")
}
let data = data.map(i => UInt8::from(i))
|> UInt8Array::from
|> NumberArrayEnum::UInt8Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data }
}
///|
fn ConstantArray::newUInt16Array(
vty : ArrayType,
data : Array[UInt16],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int16Type(_) else {
llvm_unreachable("ConstantArray::new: elementType is not Int16Type")
}
let data = data
|> UInt16Array::from
|> NumberArrayEnum::UInt16Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data }
}
///|
fn ConstantArray::newUInt32Array(
vty : ArrayType,
data : Array[UInt],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int32Type(_) else {
llvm_unreachable("ConstantArray::new: elementType is not Int32Type")
}
let data = data
|> UInt32Array::from
|> NumberArrayEnum::UInt32Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data }
}
///|
fn ConstantArray::newUInt64Array(
vty : ArrayType,
data : Array[UInt64],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int64Type(_) else {
llvm_unreachable("ConstantArray::new: elementType is not Int64Type")
}
let data = data
|> UInt64Array::from
|> NumberArrayEnum::UInt64Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data }
}
///|
fn ConstantArray::newFloatArray(
vty : ArrayType,
data : Array[Float],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is FloatType(_) else {
llvm_unreachable("ConstantArray::new: elementType is not FloatType")
}
let data = data
|> FloatArray::from
|> NumberArrayEnum::FloatArray
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data }
}
///|
fn ConstantArray::newDoubleArray(
vty : ArrayType,
data : Array[Double],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is DoubleType(_) else {
llvm_unreachable("ConstantArray::new: elementType is not DoubleType")
}
let data = data
|> DoubleArray::from
|> NumberArrayEnum::DoubleArray
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data }
}
///|
fn ConstantArray::newConstantArray(
vty : ArrayType,
data : Array[&Constant],
) -> ConstantArray {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantArray::new: data length is not equal to element count",
)
}
let eleTy = vty.elementType
let consistency = data
.map(d => d.getType())
.fold(init=true, fn(acc, ty) { acc && ty == eleTy })
guard consistency else {
llvm_unreachable(
"ConstantArray::new: data type is not consistent with elementType:",
)
}
let uid = valueUIDAssigner.assign()
ConstantArray::{ uid, vty, data: Either::Left(data) }
}
///|
pub impl Value for ConstantArray with getValueBase(self) {
ValueBase::{ uid: self.uid, vty: self.vty, users: [] }
}
///|
pub impl Value for ConstantArray with asValueEnum(self) {
ConstantArray(self)
}
///|
pub impl Value for ConstantArray with getValueRepr(self) {
match self.data {
Left(constants) => constants.map(c => c.to_string()).join(", ")
Right(arr) =>
match arr {
Int8Array(i8arr) => i8arr.iter().map(i => "i8 \{i}").join(", ")
Int16Array(i16arr) => i16arr.iter().map(i => "i16 \{i}").join(", ")
Int32Array(i32arr) => i32arr.iter().map(i => "i32 \{i}").join(", ")
Int64Array(i64arr) => i64arr.iter().map(i => "i64 \{i}").join(", ")
UInt8Array(u8arr) => u8arr.iter().map(i => "i8 \{i}").join(", ")
UInt16Array(u16arr) => u16arr.iter().map(i => "i16 \{i}").join(", ")
UInt32Array(u32arr) => u32arr.iter().map(i => "i32 \{i}").join(", ")
UInt64Array(u64arr) => u64arr.iter().map(i => "i64 \{i}").join(", ")
FloatArray(farr) => {
fn to_str(f : Float) {
let nd = f.to_double()
let ud = nd.reinterpret_as_uint64()
let sd = ud.to_string(radix=16).to_upper()
"float 0x\{sd}"
}
farr.iter().map(to_str).join(", ")
}
DoubleArray(darr) => {
fn to_str(nd : Double) {
let ud = nd.reinterpret_as_uint64()
let sd = ud.to_string(radix=16).to_upper()
"double 0x\{sd}"
}
darr.iter().map(to_str).join(", ")
}
}
}
}
///|
pub impl Value for ConstantArray with getName(_) {
None
}
///|
pub impl Value for ConstantArray with setName(_, _) {
let msg = "Calling always failed function `ConstantArray::setName`. " +
"Set name for ConstantArray is not allowed."
raise LLVMValueError(msg)
}
///|
pub impl Value for ConstantArray with removeName(_) {
()
}
///|
pub impl Value for ConstantArray with getNameOrSlot(_) {
None
}
///|
pub impl Constant for ConstantArray with asConstantEnum(self) {
ConstantArray(self)
}
///|
pub impl Show for ConstantArray with output(self, logger) {
let data_str = self.getValueRepr()
logger.write_string("\{self.getType()} [\{data_str}]")
}
// ====================================================================
// ConstantVector
// ====================================================================
///|
pub struct ConstantVector {
uid : UInt64
vty : VectorType
data : Either[Array[&Constant], NumberArrayEnum]
} derive(Eq)
///|
//fn ConstantVector::newConstantVector(
// vty : &Type,
// data : Array[&Constant]
//) -> ConstantVector {
// guard vty.asTypeEnum() is VectorType(arrTy) else {
// llvm_unreachable("ConstantVector::new: vty is not VectorType")
// }
// let eleCnt = arrTy.getElementCount().reinterpret_as_int()
// guard data.length() == eleCnt else {
// llvm_unreachable(
// "ConstantVector::new: data length is not equal to element count",
// )
// }
// let eleTy = arrTy.getElementType()
// let consistency = data
// .map(d => d.getType())
// .fold(init=true, fn(acc, ty) { acc && ty == eleTy })
// guard consistency else {
// llvm_unreachable(
// "ConstantVector::new: data type is not consistent with elementType",
// )
// }
// let base = ValueBase::new(vty)
// ConstantVector::{ base, data: Either::Left(data) }
//}
// TODO: use `Int8` instead of `Int` when standard library support `Int8`
///|
fn ConstantVector::newInt8Vector(
vty : VectorType,
data : Array[Int],
) -> ConstantVector {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantVector::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int8Type(_) else {
llvm_unreachable("ConstantVector::new: elementType is not Int8Type")
}
let data = data.map(i => Int8::from(i))
|> Int8Array::from
|> NumberArrayEnum::Int8Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantVector::{ uid, vty, data }
}
///|
fn ConstantVector::newInt16Vector(
vty : VectorType,
data : Array[Int16],
) -> ConstantVector {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantVector::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int16Type(_) else {
llvm_unreachable("ConstantVector::new: elementType is not Int16Type")
}
let data = data
|> Int16Array::from
|> NumberArrayEnum::Int16Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantVector::{ uid, vty, data }
}
///|
fn ConstantVector::newInt32Vector(
vty : VectorType,
data : Array[Int],
) -> ConstantVector {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantVector::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int32Type(_) else {
llvm_unreachable("ConstantVector::new: elementType is not Int32Type")
}
let data = data
|> Int32Array::from
|> NumberArrayEnum::Int32Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantVector::{ uid, vty, data }
}
///|
fn ConstantVector::newInt64Vector(
vty : VectorType,
data : Array[Int64],
) -> ConstantVector {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantVector::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int64Type(_) else {
llvm_unreachable("ConstantVector::new: elementType is not Int64Type")
}
let data = data
|> Int64Array::from
|> NumberArrayEnum::Int64Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantVector::{ uid, vty, data }
}
///|
fn ConstantVector::newUInt8Vector(
vty : VectorType,
data : Array[Byte],
) -> ConstantVector {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantVector::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int8Type(_) else {
llvm_unreachable("ConstantVector::new: elementType is not Int8Type")
}
let data = data.map(i => UInt8::from(i))
|> UInt8Array::from
|> NumberArrayEnum::UInt8Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantVector::{ uid, vty, data }
}
///|
fn ConstantVector::newUInt16Vector(
vty : VectorType,
data : Array[UInt16],
) -> ConstantVector {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantVector::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int16Type(_) else {
llvm_unreachable("ConstantVector::new: elementType is not Int16Type")
}
let data = data
|> UInt16Array::from
|> NumberArrayEnum::UInt16Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantVector::{ uid, vty, data }
}
///|
fn ConstantVector::newUInt32Vector(
vty : VectorType,
data : Array[UInt],
) -> ConstantVector {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantVector::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int32Type(_) else {
llvm_unreachable("ConstantVector::new: elementType is not Int32Type")
}
let data = data
|> UInt32Array::from
|> NumberArrayEnum::UInt32Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantVector::{ uid, vty, data }
}
///|
fn ConstantVector::newUInt64Vector(
vty : VectorType,
data : Array[UInt64],
) -> ConstantVector {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantVector::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is Int64Type(_) else {
llvm_unreachable("ConstantVector::new: elementType is not Int64Type")
}
let data = data
|> UInt64Array::from
|> NumberArrayEnum::UInt64Array
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantVector::{ uid, vty, data }
}
///|
fn ConstantVector::newFloatVector(
vty : VectorType,
data : Array[Float],
) -> ConstantVector {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantVector::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is FloatType(_) else {
llvm_unreachable("ConstantVector::new: elementType is not FloatType")
}
let data = data
|> FloatArray::from
|> NumberArrayEnum::FloatArray
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantVector::{ uid, vty, data }
}
///|
fn ConstantVector::newDoubleVector(
vty : VectorType,
data : Array[Double],
) -> ConstantVector {
let eleCnt = vty.getElementCount()
guard data.length() == eleCnt else {
llvm_unreachable(
"ConstantVector::new: data length is not equal to element count",
)
}
guard vty.elementType.asTypeEnum() is DoubleType(_) else {
llvm_unreachable("ConstantVector::new: elementType is not DoubleType")
}
let data = data
|> DoubleArray::from
|> NumberArrayEnum::DoubleArray
|> Either::Right
let uid = valueUIDAssigner.assign()
ConstantVector::{ uid, vty, data }
}
///|
pub impl Value for ConstantVector with getValueBase(self) {
ValueBase::{ uid: self.uid, vty: self.vty, users: [] }
}
///|
pub impl Value for ConstantVector with asValueEnum(self) {
ConstantVector(self)
}
///|
pub impl Value for ConstantVector with getValueRepr(self) {
match self.data {
Left(constants) => constants.map(c => c.to_string()).join(", ")
Right(arr) =>
match arr {
Int8Array(i8arr) => i8arr.iter().map(i => "i8 \{i}").join(", ")
Int16Array(i16arr) => i16arr.iter().map(i => "i16 \{i}").join(", ")
Int32Array(i32arr) => i32arr.iter().map(i => "i32 \{i}").join(", ")
Int64Array(i64arr) => i64arr.iter().map(i => "i64 \{i}").join(", ")
UInt8Array(u8arr) => u8arr.iter().map(i => "i8 \{i}").join(", ")
UInt16Array(u16arr) => u16arr.iter().map(i => "i16 \{i}").join(", ")
UInt32Array(u32arr) => u32arr.iter().map(i => "i32 \{i}").join(", ")
UInt64Array(u64arr) => u64arr.iter().map(i => "i64 \{i}").join(", ")
FloatArray(farr) => {
fn to_str(f : Float) {
let nd = f.to_double()
let ud = nd.reinterpret_as_uint64()
let sd = ud.to_string(radix=16).to_upper()
"float 0x\{sd}"
}
farr.iter().map(to_str).join(", ")
}
DoubleArray(darr) => {
fn to_str(nd : Double) {
let ud = nd.reinterpret_as_uint64()
let sd = ud.to_string(radix=16).to_upper()
"double 0x\{sd}"
}
darr.iter().map(to_str).join(", ")
}
}
}
}
///|
pub impl Value for ConstantVector with getName(_) {
None
}
///|
pub impl Value for ConstantVector with setName(_, _) {
let msg = "Calling always failed function `ConstantVector::setName`. " +
"Set name for ConstantVector is not allowed."
raise LLVMValueError(msg)
}
///|
pub impl Value for ConstantVector with removeName(_) {
()
}
///|
pub impl Value for ConstantVector with getNameOrSlot(_) {
None
}
///|
pub impl Constant for ConstantVector with asConstantEnum(self) {
ConstantVector(self)
}
///|
pub impl Show for ConstantVector with output(self, logger) {
let data_str = self.getValueRepr()
logger.write_string("\{self.getType()} <\{data_str}>")
}
// ====================================================================
// ConstantStruct
// ====================================================================
///|
/// ConstantStruct
///
/// Represents a constant struct value in LLVM IR.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i32ty = ctx.getInt32Ty()
/// let f32ty = ctx.getFloatTy()
/// let sty = ctx.getStructType([i32ty, f32ty])
/// let i32_val = ctx.getConstInt32(42)
/// let f32_val = ctx.getConstFloat(3.14)
/// let struct_val = ConstantStruct::new(sty, [i32_val, f32_val])
/// inspect(
/// struct_val,
/// content="{ i32, float } { i32 42, float 0x40091EB860000000 }",
/// )
/// }
/// ```
pub struct ConstantStruct {
// Unique identifier
uid : UInt64
// Value type, must be a struct type
vty : StructType
elements : Array[&Constant]
} derive(Eq)
///|
pub fn ConstantStruct::new(
vty : StructType,
elements : Array[&Constant],
) -> ConstantStruct {
guard vty.asTypeEnum() is StructType(sty) else {
llvm_unreachable("ConstantStruct::new: vty is not StructType")
}
let struct_elements = sty.elements()
guard elements.length() == struct_elements.length() else {
llvm_unreachable("ConstantStruct::new: elements length mismatch")
}
for i = 0; i < elements.length(); i = i + 1 {
guard elements[i].getType() == struct_elements[i] else {
llvm_unreachable(
"ConstantStruct::new: element type mismatch at index \{i}",
)
}
}
let uid = valueUIDAssigner.assign()
ConstantStruct::{ uid, vty, elements: elements.copy() }
}
///|
/// Get the elements of this constant struct.
pub fn ConstantStruct::getElements(self : ConstantStruct) -> Array[&Constant] {
self.elements.copy()
}
///|
/// Get the element at the specified index.
pub fn ConstantStruct::getElement(
self : ConstantStruct,
index : Int,
) -> &Constant? {
if index >= 0 && index < self.elements.length() {
Some(self.elements[index])
} else {
None
}
}
///|
/// Extract a value from this constant struct using a sequence of indices.
///
/// This is equivalent to LLVM's `extractvalue` instruction for constant structs.
/// The indices specify a path through nested aggregate types.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i32ty = ctx.getInt32Ty()
/// let f32ty = ctx.getFloatTy()
/// let sty = ctx.getStructType([i32ty, f32ty])
/// let i32_val = ctx.getConstInt32(42)
/// let f32_val = ctx.getConstFloat(3.14)
/// let struct_val = ConstantStruct::new(sty, [i32_val, f32_val])
/// inspect(struct_val.extractValue([0]), content="Some(i32 42)")
/// inspect(
/// struct_val.extractValue([1]),
/// content="Some(float 0x40091EB860000000)",
/// )
/// inspect(struct_val.extractValue([2]), content="None") // Out of bounds
/// }
/// ```
pub fn ConstantStruct::extractValue(
self : ConstantStruct,
indices : ArrayView[Int],
) -> &Constant? {
guard indices.length() > 0 else { return None }
let first_index = indices[0]
guard first_index >= 0 && first_index < self.elements.length() else {
return None
}
let element = self.elements[first_index]
if indices.length() == 1 {
// Base case: return the element
Some(element)
} else {
// Recursive case: extract from the nested aggregate
match element.asConstantEnum() {
ConstantStruct(nested_struct) => nested_struct.extractValue(indices[1:])
_ => None // For now, only support nested structs
}
}
}
///|
/// Insert a value into this constant struct using a sequence of indices.
///
/// This is equivalent to LLVM's `insertvalue` instruction for constant structs.
/// Returns a new ConstantStruct with the value inserted at the specified path.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i32ty = ctx.getInt32Ty()
/// let f32ty = ctx.getFloatTy()
/// let sty = ctx.getStructType([i32ty, f32ty])
/// let i32_val = ctx.getConstInt32(42)
/// let f32_val = ctx.getConstFloat(3.14)
/// let struct_val = ConstantStruct::new(sty, [i32_val, f32_val])
///
/// // Insert new value at index 0
/// let new_i32_val = ctx.getConstInt32(100)
/// guard struct_val.insertValue([0], new_i32_val) is Some(new_struct)
///
/// // Verify the new value was inserted
/// assert_true(new_struct.extractValue([0]) is Some(_))
/// }
/// ```
/// // TODO: not correct
pub fn ConstantStruct::insertValue(
self : ConstantStruct,
indices : ArrayView[Int],
value : &Constant,
) -> ConstantStruct? {
let first_index = indices[0]
guard first_index >= 0 && first_index < self.elements.length() else {
return None
}
if indices.length() == 1 {
// Base case: replace the element at this index
guard self.getType().asTypeEnum() is StructType(sty) else { return None }
guard value.getType() == sty.elements()[first_index] else { return None }
let new_elements = self.elements.copy()
new_elements[first_index] = value
Some(ConstantStruct::new(self.vty, new_elements))
} else {
// Recursive case: insert into the nested aggregate
let element = self.elements[first_index]
let new_element = match element.asConstantEnum() {
ConstantStruct(nested_struct) =>
match nested_struct.insertValue(indices[1:], value) {
Some(new_nested) => Some((new_nested : &Constant))
None => None
}
_ => None // For now, only support nested structs
}
match new_element {
Some(elem) => {
let new_elements = self.elements.copy()
new_elements[first_index] = elem
Some(ConstantStruct::new(self.vty, new_elements))
}
None => None
}
}
}
///|
pub impl Value for ConstantStruct with getValueBase(self) {
ValueBase::{ uid: self.uid, vty: self.vty, users: [] }
}
///|
pub impl Value for ConstantStruct with asValueEnum(self) {
ConstantStruct(self)
}
///|
pub impl Value for ConstantStruct with getValueRepr(self) {
self.elements.map(c => c.to_string()).join(", ")
}
///|
pub impl Value for ConstantStruct with getName(_) {
None
}
///|
pub impl Value for ConstantStruct with setName(_, _) {
let msg = "Calling always failed function `ConstantStruct::setName`. " +
"Set name for ConstantStruct is not allowed."
raise LLVMValueError(msg)
}
///|
pub impl Value for ConstantStruct with removeName(_) {
()
}
///|
pub impl Value for ConstantStruct with getNameOrSlot(_) {
None
}
///|
pub impl Constant for ConstantStruct with asConstantEnum(self) {
ConstantStruct(self)
}
///|
pub impl Show for ConstantStruct with output(self, logger) {
let data_str = self.getValueRepr()
logger.write_string("\{self.getType()} { \{data_str} }")
}
// ====================================================================
// ConstantString
// ====================================================================
///|
pub struct ConstantString {
// unique identifier for this value
uid : UInt64
// Value Type, vty must be ArrayType of Int8Type
vty : ArrayType
// the value of this constant
data : String
} derive(Eq)
///|
fn ConstantString::new(ctx : Context, data : String) -> ConstantString {
let vty = try! ctx.getArrayType(ctx.getInt8Ty(), data.length() + 1)
let uid = valueUIDAssigner.assign()
ConstantString::{ uid, vty, data }
}
///|
pub impl Value for ConstantString with getValueBase(self) -> ValueBase {
ValueBase::{ uid: self.uid, vty: self.vty, users: [] }
}
///|
pub impl Value for ConstantString with asValueEnum(self) -> ValueEnum {
ConstantString(self)
}
///|
pub impl Value for ConstantString with getValueRepr(self) -> String {
let str_builder = StringBuilder::new()
for c in self.data {
match c {
'\n' => str_builder.write_string("\\0A")
'\r' => str_builder.write_string("\\0D")
'\t' => str_builder.write_string("\\09")
'\"' => str_builder.write_string("\\22")
'\\' => str_builder.write_string("\\5C")
c => str_builder.write_char(c)
}
}
str_builder.write_string("\\00")
let s = str_builder.to_string()
"c\"\{s}\""
}
///|
pub impl Value for ConstantString with getName(_) {
None
}
///|
pub impl Value for ConstantString with setName(_, _) {
let msg = "Calling always failed function `ConstantString::setName`. " +
"Set name for ConstantString is not allowed."
raise LLVMValueError(msg)
}
///|
pub impl Value for ConstantString with removeName(_) {
()
}
///|
pub impl Value for ConstantString with getNameOrSlot(_) {
None
}
///|
pub impl Constant for ConstantString with asConstantEnum(self) -> ConstantEnum {
ConstantString(self)
}
///|
pub impl Show for ConstantString with output(self, logger) {
let val_str = self.getValueRepr()
let len = self.data.length() + 1
logger.write_string("[\{len} x i8] \{val_str}")
}