use of io.airlift.bytecode.BytecodeNode in project hetu-core by openlookeng.
the class AndCodeGenerator method generateExpression.
@Override
public BytecodeNode generateExpression(FunctionHandle functionHandle, BytecodeGeneratorContext generator, Type returnType, List<RowExpression> arguments) {
Preconditions.checkArgument(arguments.size() == 2);
Variable wasNull = generator.wasNull();
BytecodeBlock block = new BytecodeBlock().comment("AND").setDescription("AND");
BytecodeNode left = generator.generate(arguments.get(0));
BytecodeNode right = generator.generate(arguments.get(1));
block.append(left);
IfStatement ifLeftIsNull = new IfStatement("if left wasNull...").condition(wasNull);
LabelNode end = new LabelNode("end");
ifLeftIsNull.ifTrue().comment("clear the null flag, pop left value off stack, and push left null flag on the stack (true)").append(wasNull.set(constantFalse())).pop(// discard left value
arguments.get(0).getType().getJavaType()).push(true);
LabelNode leftIsTrue = new LabelNode("leftIsTrue");
ifLeftIsNull.ifFalse().comment("if left is false, push false, and goto end").ifTrueGoto(leftIsTrue).push(false).gotoLabel(end).comment("left was true; push left null flag on the stack (false)").visitLabel(leftIsTrue).push(false);
block.append(ifLeftIsNull);
// At this point we know the left expression was either NULL or TRUE. The stack contains a single boolean
// value for this expression which indicates if the left value was NULL.
// eval right!
block.append(right);
IfStatement ifRightIsNull = new IfStatement("if right wasNull...");
ifRightIsNull.condition().append(wasNull);
// this leaves a single boolean on the stack which is ignored since the value in NULL
ifRightIsNull.ifTrue().comment("right was null, pop the right value off the stack; wasNull flag remains set to TRUE").pop(arguments.get(1).getType().getJavaType());
LabelNode rightIsTrue = new LabelNode("rightIsTrue");
ifRightIsNull.ifFalse().comment("if right is false, pop left null flag off stack, push false and goto end").ifTrueGoto(rightIsTrue).pop(boolean.class).push(false).gotoLabel(end).comment("right was true; store left null flag (on stack) in wasNull variable, and push true").visitLabel(rightIsTrue).putVariable(wasNull).push(true);
block.append(ifRightIsNull).visitLabel(end);
return block;
}
use of io.airlift.bytecode.BytecodeNode in project hetu-core by openlookeng.
the class MapFilterFunction method generateFilter.
private static MethodHandle generateFilter(MapType mapType) {
CallSiteBinder binder = new CallSiteBinder();
Type keyType = mapType.getKeyType();
Type valueType = mapType.getValueType();
Class<?> keyJavaType = Primitives.wrap(keyType.getJavaType());
Class<?> valueJavaType = Primitives.wrap(valueType.getJavaType());
ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName("MapFilter"), type(Object.class));
definition.declareDefaultConstructor(a(PRIVATE));
Parameter state = arg("state", Object.class);
Parameter block = arg("block", Block.class);
Parameter function = arg("function", BinaryFunctionInterface.class);
MethodDefinition method = definition.declareMethod(a(PUBLIC, STATIC), "filter", type(Block.class), ImmutableList.of(state, block, function));
BytecodeBlock body = method.getBody();
Scope scope = method.getScope();
Variable positionCount = scope.declareVariable(int.class, "positionCount");
Variable position = scope.declareVariable(int.class, "position");
Variable pageBuilder = scope.declareVariable(PageBuilder.class, "pageBuilder");
Variable mapBlockBuilder = scope.declareVariable(BlockBuilder.class, "mapBlockBuilder");
Variable singleMapBlockWriter = scope.declareVariable(BlockBuilder.class, "singleMapBlockWriter");
Variable keyElement = scope.declareVariable(keyJavaType, "keyElement");
Variable valueElement = scope.declareVariable(valueJavaType, "valueElement");
Variable keep = scope.declareVariable(Boolean.class, "keep");
// invoke block.getPositionCount()
body.append(positionCount.set(block.invoke("getPositionCount", int.class)));
// prepare the single map block builder
body.append(pageBuilder.set(state.cast(PageBuilder.class)));
body.append(new IfStatement().condition(pageBuilder.invoke("isFull", boolean.class)).ifTrue(pageBuilder.invoke("reset", void.class)));
body.append(mapBlockBuilder.set(pageBuilder.invoke("getBlockBuilder", BlockBuilder.class, constantInt(0))));
body.append(singleMapBlockWriter.set(mapBlockBuilder.invoke("beginBlockEntry", BlockBuilder.class)));
SqlTypeBytecodeExpression keySqlType = constantType(binder, keyType);
BytecodeNode loadKeyElement;
if (!keyType.equals(UNKNOWN)) {
// key element must be non-null
loadKeyElement = new BytecodeBlock().append(keyElement.set(keySqlType.getValue(block, position).cast(keyJavaType)));
} else {
loadKeyElement = new BytecodeBlock().append(keyElement.set(constantNull(keyJavaType)));
}
SqlTypeBytecodeExpression valueSqlType = constantType(binder, valueType);
BytecodeNode loadValueElement;
if (!valueType.equals(UNKNOWN)) {
loadValueElement = new IfStatement().condition(block.invoke("isNull", boolean.class, add(position, constantInt(1)))).ifTrue(valueElement.set(constantNull(valueJavaType))).ifFalse(valueElement.set(valueSqlType.getValue(block, add(position, constantInt(1))).cast(valueJavaType)));
} else {
loadValueElement = new BytecodeBlock().append(valueElement.set(constantNull(valueJavaType)));
}
body.append(new ForLoop().initialize(position.set(constantInt(0))).condition(lessThan(position, positionCount)).update(incrementVariable(position, (byte) 2)).body(new BytecodeBlock().append(loadKeyElement).append(loadValueElement).append(keep.set(function.invoke("apply", Object.class, keyElement.cast(Object.class), valueElement.cast(Object.class)).cast(Boolean.class))).append(new IfStatement("if (keep != null && keep) ...").condition(and(notEqual(keep, constantNull(Boolean.class)), keep.cast(boolean.class))).ifTrue(new BytecodeBlock().append(keySqlType.invoke("appendTo", void.class, block, position, singleMapBlockWriter)).append(valueSqlType.invoke("appendTo", void.class, block, add(position, constantInt(1)), singleMapBlockWriter))))));
body.append(mapBlockBuilder.invoke("closeEntry", BlockBuilder.class).pop());
body.append(pageBuilder.invoke("declarePosition", void.class));
body.append(constantType(binder, mapType).invoke("getObject", Object.class, mapBlockBuilder.cast(Block.class), subtract(mapBlockBuilder.invoke("getPositionCount", int.class), constantInt(1))).ret());
Class<?> generatedClass = defineClass(definition, Object.class, binder.getBindings(), MapFilterFunction.class.getClassLoader());
return methodHandle(generatedClass, "filter", Object.class, Block.class, BinaryFunctionInterface.class);
}
use of io.airlift.bytecode.BytecodeNode in project hetu-core by openlookeng.
the class MapTransformValueFunction method generateTransform.
private static MethodHandle generateTransform(Type keyType, Type valueType, Type transformedValueType, Type resultMapType) {
CallSiteBinder binder = new CallSiteBinder();
Class<?> keyJavaType = Primitives.wrap(keyType.getJavaType());
Class<?> valueJavaType = Primitives.wrap(valueType.getJavaType());
Class<?> transformedValueJavaType = Primitives.wrap(transformedValueType.getJavaType());
ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName("MapTransformValue"), type(Object.class));
definition.declareDefaultConstructor(a(PRIVATE));
// define transform method
Parameter state = arg("state", Object.class);
Parameter block = arg("block", Block.class);
Parameter function = arg("function", BinaryFunctionInterface.class);
MethodDefinition method = definition.declareMethod(a(PUBLIC, STATIC), "transform", type(Block.class), ImmutableList.of(state, block, function));
BytecodeBlock body = method.getBody();
Scope scope = method.getScope();
Variable positionCount = scope.declareVariable(int.class, "positionCount");
Variable position = scope.declareVariable(int.class, "position");
Variable pageBuilder = scope.declareVariable(PageBuilder.class, "pageBuilder");
Variable mapBlockBuilder = scope.declareVariable(BlockBuilder.class, "mapBlockBuilder");
Variable blockBuilder = scope.declareVariable(BlockBuilder.class, "blockBuilder");
Variable keyElement = scope.declareVariable(keyJavaType, "keyElement");
Variable valueElement = scope.declareVariable(valueJavaType, "valueElement");
Variable transformedValueElement = scope.declareVariable(transformedValueJavaType, "transformedValueElement");
// invoke block.getPositionCount()
body.append(positionCount.set(block.invoke("getPositionCount", int.class)));
// prepare the single map block builder
body.append(pageBuilder.set(state.cast(PageBuilder.class)));
body.append(new IfStatement().condition(pageBuilder.invoke("isFull", boolean.class)).ifTrue(pageBuilder.invoke("reset", void.class)));
body.append(mapBlockBuilder.set(pageBuilder.invoke("getBlockBuilder", BlockBuilder.class, constantInt(0))));
body.append(blockBuilder.set(mapBlockBuilder.invoke("beginBlockEntry", BlockBuilder.class)));
// throw null key exception block
BytecodeNode throwNullKeyException = new BytecodeBlock().append(newInstance(PrestoException.class, getStatic(INVALID_FUNCTION_ARGUMENT.getDeclaringClass(), "INVALID_FUNCTION_ARGUMENT").cast(ErrorCodeSupplier.class), constantString("map key cannot be null"))).throwObject();
SqlTypeBytecodeExpression keySqlType = constantType(binder, keyType);
BytecodeNode loadKeyElement;
if (!keyType.equals(UNKNOWN)) {
loadKeyElement = new BytecodeBlock().append(keyElement.set(keySqlType.getValue(block, position).cast(keyJavaType)));
} else {
// make sure invokeExact will not take uninitialized keys during compile time
// but if we reach this point during runtime, it is an exception
// also close the block builder before throwing as we may be in a TRY() call
// so that subsequent calls do not find it in an inconsistent state
loadKeyElement = new BytecodeBlock().append(mapBlockBuilder.invoke("closeEntry", BlockBuilder.class).pop()).append(keyElement.set(constantNull(keyJavaType))).append(throwNullKeyException);
}
SqlTypeBytecodeExpression valueSqlType = constantType(binder, valueType);
BytecodeNode loadValueElement;
if (!valueType.equals(UNKNOWN)) {
loadValueElement = new IfStatement().condition(block.invoke("isNull", boolean.class, add(position, constantInt(1)))).ifTrue(valueElement.set(constantNull(valueJavaType))).ifFalse(valueElement.set(valueSqlType.getValue(block, add(position, constantInt(1))).cast(valueJavaType)));
} else {
loadValueElement = new BytecodeBlock().append(valueElement.set(constantNull(valueJavaType)));
}
BytecodeNode writeTransformedValueElement;
if (!transformedValueType.equals(UNKNOWN)) {
writeTransformedValueElement = new IfStatement().condition(equal(transformedValueElement, constantNull(transformedValueJavaType))).ifTrue(blockBuilder.invoke("appendNull", BlockBuilder.class).pop()).ifFalse(constantType(binder, transformedValueType).writeValue(blockBuilder, transformedValueElement.cast(transformedValueType.getJavaType())));
} else {
writeTransformedValueElement = new BytecodeBlock().append(blockBuilder.invoke("appendNull", BlockBuilder.class).pop());
}
body.append(new ForLoop().initialize(position.set(constantInt(0))).condition(lessThan(position, positionCount)).update(incrementVariable(position, (byte) 2)).body(new BytecodeBlock().append(loadKeyElement).append(loadValueElement).append(transformedValueElement.set(function.invoke("apply", Object.class, keyElement.cast(Object.class), valueElement.cast(Object.class)).cast(transformedValueJavaType))).append(keySqlType.invoke("appendTo", void.class, block, position, blockBuilder)).append(writeTransformedValueElement)));
body.append(mapBlockBuilder.invoke("closeEntry", BlockBuilder.class).pop());
body.append(pageBuilder.invoke("declarePosition", void.class));
body.append(constantType(binder, resultMapType).invoke("getObject", Object.class, mapBlockBuilder.cast(Block.class), subtract(mapBlockBuilder.invoke("getPositionCount", int.class), constantInt(1))).ret());
Class<?> generatedClass = defineClass(definition, Object.class, binder.getBindings(), MapTransformValueFunction.class.getClassLoader());
return methodHandle(generatedClass, "transform", Object.class, Block.class, BinaryFunctionInterface.class);
}
use of io.airlift.bytecode.BytecodeNode in project trino by trinodb.
the class InCodeGenerator method buildInCase.
private static BytecodeBlock buildInCase(BytecodeGeneratorContext generatorContext, Scope scope, ResolvedFunction equals, LabelNode matchLabel, LabelNode noMatchLabel, Variable value, Collection<BytecodeNode> testValues, boolean checkForNulls, ResolvedFunction isIndeterminateFunction) {
// caseWasNull is set to true the first time a null in `testValues` is encountered
Variable caseWasNull = null;
if (checkForNulls) {
caseWasNull = scope.createTempVariable(boolean.class);
}
BytecodeBlock caseBlock = new BytecodeBlock();
if (checkForNulls) {
caseBlock.putVariable(caseWasNull, false);
}
LabelNode elseLabel = new LabelNode("else");
BytecodeBlock elseBlock = new BytecodeBlock().visitLabel(elseLabel);
Variable wasNull = generatorContext.wasNull();
if (checkForNulls) {
// That is incorrect. Doing an explicit check for indeterminate is required to correctly return NULL.
if (testValues.isEmpty()) {
elseBlock.append(new BytecodeBlock().append(generatorContext.generateCall(isIndeterminateFunction, ImmutableList.of(value))).putVariable(wasNull));
} else {
elseBlock.append(wasNull.set(caseWasNull));
}
}
elseBlock.gotoLabel(noMatchLabel);
BytecodeNode elseNode = elseBlock;
for (BytecodeNode testNode : testValues) {
LabelNode testLabel = new LabelNode("test");
IfStatement test = new IfStatement();
BytecodeNode equalsCall = generatorContext.generateCall(equals, ImmutableList.of(value, testNode));
test.condition().visitLabel(testLabel).append(equalsCall);
if (checkForNulls) {
IfStatement wasNullCheck = new IfStatement("if wasNull, set caseWasNull to true, clear wasNull, pop boolean, and goto next test value");
wasNullCheck.condition(wasNull);
wasNullCheck.ifTrue(new BytecodeBlock().append(caseWasNull.set(constantTrue())).append(wasNull.set(constantFalse())).pop(boolean.class).gotoLabel(elseLabel));
test.condition().append(wasNullCheck);
}
test.ifTrue().gotoLabel(matchLabel);
test.ifFalse(elseNode);
elseNode = test;
elseLabel = testLabel;
}
caseBlock.append(elseNode);
return caseBlock;
}
use of io.airlift.bytecode.BytecodeNode in project trino by trinodb.
the class InCodeGenerator method generateExpression.
@Override
public BytecodeNode generateExpression(BytecodeGeneratorContext generatorContext) {
Type type = valueExpression.getType();
Class<?> javaType = type.getJavaType();
SwitchGenerationCase switchGenerationCase = checkSwitchGenerationCase(type, testExpressions);
MethodHandle equalsMethodHandle = generatorContext.getScalarFunctionInvoker(resolvedEqualsFunction, simpleConvention(NULLABLE_RETURN, NEVER_NULL, NEVER_NULL)).getMethodHandle();
MethodHandle hashCodeMethodHandle = generatorContext.getScalarFunctionInvoker(resolvedHashCodeFunction, simpleConvention(FAIL_ON_NULL, NEVER_NULL)).getMethodHandle();
MethodHandle indeterminateMethodHandle = generatorContext.getScalarFunctionInvoker(resolvedIsIndeterminate, simpleConvention(FAIL_ON_NULL, NEVER_NULL)).getMethodHandle();
ImmutableListMultimap.Builder<Integer, BytecodeNode> hashBucketsBuilder = ImmutableListMultimap.builder();
ImmutableList.Builder<BytecodeNode> defaultBucket = ImmutableList.builder();
ImmutableSet.Builder<Object> constantValuesBuilder = ImmutableSet.builder();
for (RowExpression testValue : testExpressions) {
BytecodeNode testBytecode = generatorContext.generate(testValue);
if (isDeterminateConstant(testValue, indeterminateMethodHandle)) {
ConstantExpression constant = (ConstantExpression) testValue;
Object object = constant.getValue();
switch(switchGenerationCase) {
case DIRECT_SWITCH:
case SET_CONTAINS:
constantValuesBuilder.add(object);
break;
case HASH_SWITCH:
try {
int hashCode = Long.hashCode((Long) hashCodeMethodHandle.invoke(object));
hashBucketsBuilder.put(hashCode, testBytecode);
} catch (Throwable throwable) {
throw new IllegalArgumentException("Error processing IN statement: error calculating hash code for " + object, throwable);
}
break;
default:
throw new IllegalArgumentException("Not supported switch generation case: " + switchGenerationCase);
}
} else {
defaultBucket.add(testBytecode);
}
}
ImmutableListMultimap<Integer, BytecodeNode> hashBuckets = hashBucketsBuilder.build();
ImmutableSet<Object> constantValues = constantValuesBuilder.build();
LabelNode end = new LabelNode("end");
LabelNode match = new LabelNode("match");
LabelNode noMatch = new LabelNode("noMatch");
LabelNode defaultLabel = new LabelNode("default");
Scope scope = generatorContext.getScope();
Variable value = scope.createTempVariable(javaType);
BytecodeNode switchBlock;
Variable expression = scope.createTempVariable(int.class);
SwitchBuilder switchBuilder = new SwitchBuilder().expression(expression);
switch(switchGenerationCase) {
case DIRECT_SWITCH:
// For these types, it's safe to not use Trino HASH_CODE and EQUAL operator.
for (Object constantValue : constantValues) {
switchBuilder.addCase(toIntExact((Long) constantValue), jump(match));
}
switchBuilder.defaultCase(jump(defaultLabel));
switchBlock = new BytecodeBlock().comment("lookupSwitch(<stackValue>))").append(new IfStatement().condition(invokeStatic(InCodeGenerator.class, "isInteger", boolean.class, value)).ifFalse(new BytecodeBlock().gotoLabel(defaultLabel))).append(expression.set(value.cast(int.class))).append(switchBuilder.build());
break;
case HASH_SWITCH:
for (Map.Entry<Integer, Collection<BytecodeNode>> bucket : hashBuckets.asMap().entrySet()) {
Collection<BytecodeNode> testValues = bucket.getValue();
BytecodeBlock caseBlock = buildInCase(generatorContext, scope, resolvedEqualsFunction, match, defaultLabel, value, testValues, false, resolvedIsIndeterminate);
switchBuilder.addCase(bucket.getKey(), caseBlock);
}
switchBuilder.defaultCase(jump(defaultLabel));
Binding hashCodeBinding = generatorContext.getCallSiteBinder().bind(hashCodeMethodHandle);
switchBlock = new BytecodeBlock().comment("lookupSwitch(hashCode(<stackValue>))").getVariable(value).append(invoke(hashCodeBinding, resolvedHashCodeFunction.getSignature())).invokeStatic(Long.class, "hashCode", int.class, long.class).putVariable(expression).append(switchBuilder.build());
break;
case SET_CONTAINS:
Set<?> constantValuesSet = toFastutilHashSet(constantValues, type, hashCodeMethodHandle, equalsMethodHandle);
Binding constant = generatorContext.getCallSiteBinder().bind(constantValuesSet, constantValuesSet.getClass());
switchBlock = new BytecodeBlock().comment("inListSet.contains(<stackValue>)").append(new IfStatement().condition(new BytecodeBlock().comment("value").getVariable(value).comment("set").append(loadConstant(constant)).invokeStatic(FastutilSetHelper.class, "in", boolean.class, javaType.isPrimitive() ? javaType : Object.class, constantValuesSet.getClass())).ifTrue(jump(match)));
break;
default:
throw new IllegalArgumentException("Not supported switch generation case: " + switchGenerationCase);
}
BytecodeBlock defaultCaseBlock = buildInCase(generatorContext, scope, resolvedEqualsFunction, match, noMatch, value, defaultBucket.build(), true, resolvedIsIndeterminate).setDescription("default");
BytecodeBlock block = new BytecodeBlock().comment("IN").append(generatorContext.generate(valueExpression)).append(ifWasNullPopAndGoto(scope, end, boolean.class, javaType)).putVariable(value).append(switchBlock).visitLabel(defaultLabel).append(defaultCaseBlock);
BytecodeBlock matchBlock = new BytecodeBlock().setDescription("match").visitLabel(match).append(generatorContext.wasNull().set(constantFalse())).push(true).gotoLabel(end);
block.append(matchBlock);
BytecodeBlock noMatchBlock = new BytecodeBlock().setDescription("noMatch").visitLabel(noMatch).push(false).gotoLabel(end);
block.append(noMatchBlock);
block.visitLabel(end);
return block;
}
Aggregations