package com.oracle.truffle.llvm.runtime.nodes.intrinsics.llvm.arith;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMExpressionNode;
import com.oracle.truffle.llvm.runtime.nodes.intrinsics.llvm.LLVMBuiltin;
import com.oracle.truffle.llvm.runtime.nodes.memory.store.LLVMI16StoreNode;
import com.oracle.truffle.llvm.runtime.nodes.memory.store.LLVMI1StoreNode.LLVMI1OffsetStoreNode;
import com.oracle.truffle.llvm.runtime.nodes.memory.store.LLVMI32StoreNode;
import com.oracle.truffle.llvm.runtime.nodes.memory.store.LLVMI64StoreNode;
import com.oracle.truffle.llvm.runtime.nodes.memory.store.LLVMI8StoreNode;
import com.oracle.truffle.llvm.runtime.pointer.LLVMPointer;
public abstract class LLVMArithmetic {
private LLVMArithmetic() {
}
public interface Arithmetic {
boolean evalI8(byte left, byte right, LLVMPointer addr, LLVMI8StoreNode store);
boolean evalI16(short left, short right, LLVMPointer addr, LLVMI16StoreNode store);
boolean evalI32(int left, int right, LLVMPointer addr, LLVMI32StoreNode store);
boolean evalI64(long left, long right, LLVMPointer addr, LLVMI64StoreNode store);
}
public interface SaturatingArithmetic {
byte evalI8(byte left, byte right);
short evalI16(short left, short right);
int evalI32(int left, int right);
long evalI64(long left, long right);
}
public interface CarryArithmetic {
byte evalI8(byte left, byte right, byte cin, LLVMPointer addr, LLVMI8StoreNode store);
short evalI16(short left, short right, short cin, LLVMPointer addr, LLVMI16StoreNode store);
int evalI32(int left, int right, int cin, LLVMPointer addr, LLVMI32StoreNode store);
long evalI64(long left, long right, long cin, LLVMPointer addr, LLVMI64StoreNode store);
}
public static final CarryArithmetic CARRY_ADD = new CarryArithmetic() {
@Override
public byte evalI8(byte left, byte right, byte cin, LLVMPointer addr, LLVMI8StoreNode store) {
final int res = (left & LLVMExpressionNode.I8_MASK) + (right & LLVMExpressionNode.I8_MASK) + (cin & LLVMExpressionNode.I8_MASK);
final boolean overflow = (res & (0xF << Byte.SIZE)) != 0;
store.executeWithTarget(addr, (byte) (overflow ? 1 : 0));
return (byte) res;
}
@Override
public short evalI16(short left, short right, short cin, LLVMPointer addr, LLVMI16StoreNode store) {
final int res = (left & LLVMExpressionNode.I16_MASK) + (right & LLVMExpressionNode.I16_MASK) + (cin & LLVMExpressionNode.I16_MASK);
final boolean overflow = (res & (0xF << Short.SIZE)) != 0;
store.executeWithTarget(addr, (short) (overflow ? 1 : 0));
return (short) res;
}
@Override
public int evalI32(int left, int right, int cin, LLVMPointer addr, LLVMI32StoreNode store) {
final int res1 = left + right;
final boolean overflow1 = ((~res1 & left) | (~res1 & right) | (left & right)) < 0;
final int res2 = res1 + cin;
final boolean overflow2 = ((~res2 & res1) | (~res2 & cin) | (res1 & cin)) < 0;
store.executeWithTarget(addr, (overflow1 | overflow2) ? 1 : 0);
return res2;
}
@Override
public long evalI64(long left, long right, long cin, LLVMPointer addr, LLVMI64StoreNode store) {
final long res1 = left + right;
final boolean overflow1 = ((~res1 & left) | (~res1 & right) | (left & right)) < 0;
final long res2 = res1 + cin;
final boolean overflow2 = ((~res2 & res1) | (~res2 & cin) | (res1 & cin)) < 0;
store.executeWithTarget(addr, (overflow1 | overflow2) ? 1 : 0);
return res2;
}
};
public static final CarryArithmetic CARRY_SUB = new CarryArithmetic() {
@Override
public byte evalI8(byte left, byte right, byte cin, LLVMPointer addr, LLVMI8StoreNode store) {
final int res = (left & LLVMExpressionNode.I8_MASK) - (right & LLVMExpressionNode.I8_MASK) - (cin & LLVMExpressionNode.I8_MASK);
final boolean overflow = res < 0;
store.executeWithTarget(addr, (byte) (overflow ? 1 : 0));
return (byte) res;
}
@Override
public short evalI16(short left, short right, short cin, LLVMPointer addr, LLVMI16StoreNode store) {
final int res = (left & LLVMExpressionNode.I16_MASK) - (right & LLVMExpressionNode.I16_MASK) - (cin & LLVMExpressionNode.I16_MASK);
final boolean overflow = res < 0;
store.executeWithTarget(addr, (short) (overflow ? 1 : 0));
return (short) res;
}
@Override
public int evalI32(int left, int right, int cin, LLVMPointer addr, LLVMI32StoreNode store) {
final int res1 = left - right;
final boolean overflow1 = Integer.compareUnsigned(left, right) < 0;
final int res2 = res1 - cin;
final boolean overflow2 = Integer.compareUnsigned(res1, cin) < 0;
store.executeWithTarget(addr, (overflow1 | overflow2) ? 1 : 0);
return res2;
}
@Override
public long evalI64(long left, long right, long cin, LLVMPointer addr, LLVMI64StoreNode store) {
final long res1 = left - right;
final boolean overflow1 = Long.compareUnsigned(left, right) < 0;
final long res2 = res1 - cin;
final boolean overflow2 = Long.compareUnsigned(res1, cin) < 0;
store.executeWithTarget(addr, (overflow1 | overflow2) ? 1 : 0);
return res2;
}
};
public static final Arithmetic SIGNED_ADD = new Arithmetic() {
@Override
public boolean evalI8(byte left, byte right, LLVMPointer addr, LLVMI8StoreNode store) {
final int res = left + right;
final boolean overflow = (((res ^ left) & (res ^ right)) & (1 << (Byte.SIZE - 1))) != 0;
store.executeWithTarget(addr, (byte) res);
return overflow;
}
@Override
public boolean evalI16(short left, short right, LLVMPointer addr, LLVMI16StoreNode store) {
final int res = left + right;
final boolean overflow = (((res ^ left) & (res ^ right)) & (1 << (Short.SIZE - 1))) != 0;
store.executeWithTarget(addr, (short) res);
return overflow;
}
@Override
public boolean evalI32(int left, int right, LLVMPointer addr, LLVMI32StoreNode store) {
int res;
boolean overflow = false;
try {
res = Math.addExact(left, right);
} catch (ArithmeticException e) {
res = left + right;
overflow = true;
}
store.executeWithTarget(addr, res);
return overflow;
}
@Override
public boolean evalI64(long left, long right, LLVMPointer addr, LLVMI64StoreNode store) {
long res;
boolean overflow = false;
try {
res = Math.addExact(left, right);
} catch (ArithmeticException e) {
res = left + right;
overflow = true;
}
store.executeWithTarget(addr, res);
return overflow;
}
};
public static final SaturatingArithmetic SIGNED_ADD_SAT = new SaturatingArithmetic() {
@Override
public byte evalI8(byte left, byte right) {
final int res = left + right;
final boolean overflow = (((res ^ left) & (res ^ right)) & (1 << (Byte.SIZE - 1))) != 0;
if (overflow) {
return left > 0 ? Byte.MAX_VALUE : Byte.MIN_VALUE;
} else {
return (byte) res;
}
}
@Override
public short evalI16(short left, short right) {
final int res = left + right;
final boolean overflow = (((res ^ left) & (res ^ right)) & (1 << (Short.SIZE - 1))) != 0;
if (overflow) {
return left > 0 ? Short.MAX_VALUE : Short.MIN_VALUE;
} else {
return (short) res;
}
}
@Override
public int evalI32(int left, int right) {
int res;
boolean overflow = false;
try {
res = Math.addExact(left, right);
} catch (ArithmeticException e) {
res = left + right;
overflow = true;
}
if (overflow) {
return left > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
} else {
return res;
}
}
@Override
public long evalI64(long left, long right) {
long res;
boolean overflow = false;
try {
res = Math.addExact(left, right);
} catch (ArithmeticException e) {
res = left + right;
overflow = true;
}
if (overflow) {
return left > 0 ? Long.MAX_VALUE : Long.MIN_VALUE;
} else {
return res;
}
}
};
public static final Arithmetic UNSIGNED_ADD = new Arithmetic() {
@Override
public boolean evalI8(byte left, byte right, LLVMPointer addr, LLVMI8StoreNode store) {
final int res = (left & LLVMExpressionNode.I8_MASK) + (right & LLVMExpressionNode.I8_MASK);
final boolean overflow = (res & (1 << Byte.SIZE)) != 0;
store.executeWithTarget(addr, (byte) res);
return overflow;
}
@Override
public boolean evalI16(short left, short right, LLVMPointer addr, LLVMI16StoreNode store) {
final int res = (left & LLVMExpressionNode.I16_MASK) + (right & LLVMExpressionNode.I16_MASK);
final boolean overflow = (res & (1 << Short.SIZE)) != 0;
store.executeWithTarget(addr, (short) res);
return overflow;
}
@Override
public boolean evalI32(int left, int right, LLVMPointer addr, LLVMI32StoreNode store) {
final int res = left + right;
final boolean overflow = ((~res & left) | (~res & right) | (left & right)) < 0;
store.executeWithTarget(addr, res);
return overflow;
}
@Override
public boolean evalI64(long left, long right, LLVMPointer addr, LLVMI64StoreNode store) {
final long res = left + right;
final boolean overflow = ((~res & left) | (~res & right) | (left & right)) < 0;
store.executeWithTarget(addr, res);
return overflow;
}
};
public static final SaturatingArithmetic UNSIGNED_ADD_SAT = new SaturatingArithmetic() {
@Override
public byte evalI8(byte left, byte right) {
final int res = (left & LLVMExpressionNode.I8_MASK) + (right & LLVMExpressionNode.I8_MASK);
final boolean overflow = (res & (1 << Byte.SIZE)) != 0;
return (byte) (overflow ? LLVMExpressionNode.I8_MASK : res);
}
@Override
public short evalI16(short left, short right) {
final int res = (left & LLVMExpressionNode.I16_MASK) + (right & LLVMExpressionNode.I16_MASK);
final boolean overflow = (res & (1 << Short.SIZE)) != 0;
return (short) (overflow ? LLVMExpressionNode.I16_MASK : res);
}
@Override
public int evalI32(int left, int right) {
final int res = left + right;
final boolean overflow = ((~res & left) | (~res & right) | (left & right)) < 0;
return overflow ? -1 : res;
}
@Override
public long evalI64(long left, long right) {
final long res = left + right;
final boolean overflow = ((~res & left) | (~res & right) | (left & right)) < 0;
return overflow ? -1 : res;
}
};
public static final Arithmetic SIGNED_SUB = new Arithmetic() {
@Override
public boolean evalI8(byte left, byte right, LLVMPointer addr, LLVMI8StoreNode store) {
final int res = left - right;
final boolean overflow = (((left ^ right) & (left ^ res)) & (1 << (Byte.SIZE - 1))) != 0;
store.executeWithTarget(addr, (byte) res);
return overflow;
}
@Override
public boolean evalI16(short left, short right, LLVMPointer addr, LLVMI16StoreNode store) {
final int res = left - right;
final boolean overflow = (((left ^ right) & (left ^ res)) & (1 << (Short.SIZE - 1))) != 0;
store.executeWithTarget(addr, (short) res);
return overflow;
}
@Override
public boolean evalI32(int left, int right, LLVMPointer addr, LLVMI32StoreNode store) {
int res;
boolean overflow = false;
try {
res = Math.subtractExact(left, right);
} catch (ArithmeticException e) {
res = left - right;
overflow = true;
}
store.executeWithTarget(addr, res);
return overflow;
}
@Override
public boolean evalI64(long left, long right, LLVMPointer addr, LLVMI64StoreNode store) {
long res;
boolean overflow = false;
try {
res = Math.subtractExact(left, right);
} catch (ArithmeticException e) {
res = left - right;
overflow = true;
}
store.executeWithTarget(addr, res);
return overflow;
}
};
public static final SaturatingArithmetic SIGNED_SUB_SAT = new SaturatingArithmetic() {
@Override
public byte evalI8(byte left, byte right) {
final int res = left - right;
final boolean overflow = (((left ^ right) & (left ^ res)) & (1 << (Byte.SIZE - 1))) != 0;
if (overflow) {
return ((left > 0) ^ (right < 0)) ? Byte.MAX_VALUE : Byte.MIN_VALUE;
} else {
return (byte) res;
}
}
@Override
public short evalI16(short left, short right) {
final int res = left - right;
final boolean overflow = (((left ^ right) & (left ^ res)) & (1 << (Short.SIZE - 1))) != 0;
if (overflow) {
return ((left > 0) ^ (right < 0)) ? Short.MAX_VALUE : Short.MIN_VALUE;
} else {
return (short) res;
}
}
@Override
public int evalI32(int left, int right) {
int res;
boolean overflow = false;
try {
res = Math.subtractExact(left, right);
} catch (ArithmeticException e) {
res = left - right;
overflow = true;
}
if (overflow) {
return ((left > 0) ^ (right < 0)) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
} else {
return res;
}
}
@Override
public long evalI64(long left, long right) {
long res;
boolean overflow = false;
try {
res = Math.subtractExact(left, right);
} catch (ArithmeticException e) {
res = left - right;
overflow = true;
}
if (overflow) {
return ((left > 0) ^ (right < 0)) ? Long.MAX_VALUE : Long.MIN_VALUE;
} else {
return res;
}
}
};
public static final Arithmetic UNSIGNED_SUB = new Arithmetic() {
@Override
public boolean evalI8(byte left, byte right, LLVMPointer addr, LLVMI8StoreNode store) {
final int res = (left & LLVMExpressionNode.I8_MASK) - (right & LLVMExpressionNode.I8_MASK);
boolean overflow = res < 0;
store.executeWithTarget(addr, (byte) res);
return overflow;
}
@Override
public boolean evalI16(short left, short right, LLVMPointer addr, LLVMI16StoreNode store) {
final int res = (left & LLVMExpressionNode.I16_MASK) - (right & LLVMExpressionNode.I16_MASK);
final boolean overflow = res < 0;
store.executeWithTarget(addr, (short) res);
return overflow;
}
@Override
public boolean evalI32(int left, int right, LLVMPointer addr, LLVMI32StoreNode store) {
final int res = left - right;
final boolean overflow = Integer.compareUnsigned(left, right) < 0;
store.executeWithTarget(addr, res);
return overflow;
}
@Override
public boolean evalI64(long left, long right, LLVMPointer addr, LLVMI64StoreNode store) {
final long res = left - right;
final boolean overflow = Long.compareUnsigned(left, right) < 0;
store.executeWithTarget(addr, res);
return overflow;
}
};
public static final SaturatingArithmetic UNSIGNED_SUB_SAT = new SaturatingArithmetic() {
@Override
public byte evalI8(byte left, byte right) {
final int res = (left & LLVMExpressionNode.I8_MASK) - (right & LLVMExpressionNode.I8_MASK);
boolean overflow = res < 0;
return (byte) (overflow ? 0 : res);
}
@Override
public short evalI16(short left, short right) {
final int res = (left & LLVMExpressionNode.I16_MASK) - (right & LLVMExpressionNode.I16_MASK);
boolean overflow = res < 0;
return (short) (overflow ? 0 : res);
}
@Override
public int evalI32(int left, int right) {
final int res = left - right;
final boolean overflow = Integer.compareUnsigned(left, right) < 0;
return overflow ? 0 : res;
}
@Override
public long evalI64(long left, long right) {
final long res = left - right;
final boolean overflow = Long.compareUnsigned(left, right) < 0;
return overflow ? 0 : res;
}
};
public static final Arithmetic SIGNED_MUL = new Arithmetic() {
@Override
public boolean evalI8(byte left, byte right, LLVMPointer addr, LLVMI8StoreNode store) {
final int res = left * right;
final boolean overflow = (byte) res != res;
store.executeWithTarget(addr, (byte) res);
return overflow;
}
@Override
public boolean evalI16(short left, short right, LLVMPointer addr, LLVMI16StoreNode store) {
final int res = left * right;
final boolean overflow = (short) res != res;
store.executeWithTarget(addr, (short) res);
return overflow;
}
@Override
public boolean evalI32(int left, int right, LLVMPointer addr, LLVMI32StoreNode store) {
int res;
boolean overflow = false;
try {
res = Math.multiplyExact(left, right);
} catch (ArithmeticException e) {
res = left * right;
overflow = true;
}
store.executeWithTarget(addr, res);
return overflow;
}
@Override
public boolean evalI64(long left, long right, LLVMPointer addr, LLVMI64StoreNode store) {
long res;
boolean overflow = false;
try {
res = Math.multiplyExact(left, right);
} catch (ArithmeticException e) {
res = left * right;
overflow = true;
}
store.executeWithTarget(addr, res);
return overflow;
}
};
public static final Arithmetic UNSIGNED_MUL = new Arithmetic() {
@Override
public boolean evalI8(byte left, byte right, LLVMPointer addr, LLVMI8StoreNode store) {
final int res = (left & LLVMExpressionNode.I8_MASK) * (right & LLVMExpressionNode.I8_MASK);
final boolean overflow = (res & LLVMExpressionNode.I8_MASK) != res;
store.executeWithTarget(addr, (byte) res);
return overflow;
}
@Override
public boolean evalI16(short left, short right, LLVMPointer addr, LLVMI16StoreNode store) {
final int res = (left & LLVMExpressionNode.I16_MASK) * (right & LLVMExpressionNode.I16_MASK);
final boolean overflow = (res & LLVMExpressionNode.I16_MASK) != res;
store.executeWithTarget(addr, (short) res);
return overflow;
}
@Override
public boolean evalI32(int left, int right, LLVMPointer addr, LLVMI32StoreNode store) {
final long res = (left & LLVMExpressionNode.I32_MASK) * (right & LLVMExpressionNode.I32_MASK);
final boolean overflow = (res & LLVMExpressionNode.I32_MASK) != res;
store.executeWithTarget(addr, (int) res);
return overflow;
}
@Override
public boolean evalI64(long left, long right, LLVMPointer addr, LLVMI64StoreNode store) {
final long res = left * right;
boolean overflow = false;
if ((left | right) >>> 31 != 0) {
if (right != 0 && Long.divideUnsigned(res, right) != left) {
overflow = true;
}
}
store.executeWithTarget(addr, res);
return overflow;
}
};
@NodeChild(value = "left", type = LLVMExpressionNode.class)
@NodeChild(value = "right", type = LLVMExpressionNode.class)
@NodeChild(value = "target", type = LLVMExpressionNode.class)
public abstract static class GCCArithmetic extends LLVMBuiltin {
private final Arithmetic arithmetic;
public GCCArithmetic(Arithmetic arithmetic) {
this.arithmetic = arithmetic;
}
@Specialization
protected byte doIntrinsic(byte left, byte right, LLVMPointer addr,
@Cached LLVMI8StoreNode store) {
return (byte) (arithmetic.evalI8(left, right, addr, store) ? 1 : 0);
}
@Specialization
protected short doIntrinsic(short left, short right, LLVMPointer addr,
@Cached LLVMI16StoreNode store) {
return (short) (arithmetic.evalI16(left, right, addr, store) ? 1 : 0);
}
@Specialization
protected int doIntrinsic(int left, int right, LLVMPointer addr,
@Cached LLVMI32StoreNode store) {
return arithmetic.evalI32(left, right, addr, store) ? 1 : 0;
}
@Specialization
protected long doIntrinsic(long left, long right, LLVMPointer addr,
@Cached LLVMI64StoreNode store) {
return arithmetic.evalI64(left, right, addr, store) ? 1 : 0;
}
}
@NodeChild(value = "left", type = LLVMExpressionNode.class)
@NodeChild(value = "right", type = LLVMExpressionNode.class)
public abstract static class LLVMSimpleArithmeticPrimitive extends LLVMBuiltin {
private final SaturatingArithmetic arithmetic;
public LLVMSimpleArithmeticPrimitive(SaturatingArithmetic arithmetic) {
this.arithmetic = arithmetic;
}
@Specialization
protected byte doIntrinsic(byte left, byte right) {
return arithmetic.evalI8(left, right);
}
@Specialization
protected short doIntrinsic(short left, short right) {
return arithmetic.evalI16(left, right);
}
@Specialization
protected int doIntrinsic(int left, int right) {
return arithmetic.evalI32(left, right);
}
@Specialization
protected long doIntrinsic(long left, long right) {
return arithmetic.evalI64(left, right);
}
}
@NodeChild(value = "left", type = LLVMExpressionNode.class)
@NodeChild(value = "right", type = LLVMExpressionNode.class)
@NodeChild(value = "target", type = LLVMExpressionNode.class)
public abstract static class LLVMArithmeticWithOverflow extends LLVMBuiltin {
private final long secondValueOffset;
private final Arithmetic arithmetic;
@Child private LLVMI1OffsetStoreNode storeI1 = LLVMI1OffsetStoreNode.create();
public LLVMArithmeticWithOverflow(Arithmetic arithmetic, long secondValueOffset) {
this.secondValueOffset = secondValueOffset;
this.arithmetic = arithmetic;
}
@Specialization
protected Object doIntrinsic(byte left, byte right, LLVMPointer addr,
@Cached LLVMI8StoreNode store) {
boolean overflow = arithmetic.evalI8(left, right, addr, store);
storeI1.executeWithTarget(addr, secondValueOffset, overflow);
return addr;
}
@Specialization
protected Object doIntrinsic(short left, short right, LLVMPointer addr,
@Cached LLVMI16StoreNode store) {
boolean overflow = arithmetic.evalI16(left, right, addr, store);
storeI1.executeWithTarget(addr, secondValueOffset, overflow);
return addr;
}
@Specialization
protected Object doIntrinsic(int left, int right, LLVMPointer addr,
@Cached LLVMI32StoreNode store) {
boolean overflow = arithmetic.evalI32(left, right, addr, store);
storeI1.executeWithTarget(addr, secondValueOffset, overflow);
return addr;
}
@Specialization
protected Object doIntrinsic(long left, long right, LLVMPointer addr,
@Cached LLVMI64StoreNode store) {
boolean overflow = arithmetic.evalI64(left, right, addr, store);
storeI1.executeWithTarget(addr, secondValueOffset, overflow);
return addr;
}
}
@NodeChild(value = "left", type = LLVMExpressionNode.class)
@NodeChild(value = "right", type = LLVMExpressionNode.class)
@NodeChild(value = "cin", type = LLVMExpressionNode.class)
@NodeChild(value = "cout", type = LLVMExpressionNode.class)
public abstract static class LLVMArithmeticWithOverflowAndCarry extends LLVMBuiltin {
private final CarryArithmetic arithmetic;
public LLVMArithmeticWithOverflowAndCarry(CarryArithmetic arithmetic) {
this.arithmetic = arithmetic;
}
@Specialization
protected byte doIntrinsic(byte left, byte right, byte cin, LLVMPointer addr,
@Cached LLVMI8StoreNode store) {
return arithmetic.evalI8(left, right, cin, addr, store);
}
@Specialization
protected short doIntrinsic(short left, short right, short cin, LLVMPointer addr,
@Cached LLVMI16StoreNode store) {
return arithmetic.evalI16(left, right, cin, addr, store);
}
@Specialization
protected int doIntrinsic(int left, int right, int cin, LLVMPointer addr,
@Cached LLVMI32StoreNode store) {
return arithmetic.evalI32(left, right, cin, addr, store);
}
@Specialization
protected long doIntrinsic(long left, long right, long cin, LLVMPointer addr,
@Cached LLVMI64StoreNode store) {
return arithmetic.evalI64(left, right, cin, addr, store);
}
}
}