use of com.facebook.presto.bytecode.control.ForLoop in project presto by prestodb.
the class MapTransformKeyFunction method generateTransformKey.
private static MethodHandle generateTransformKey(Type keyType, Type transformedKeyType, Type valueType, Type resultMapType) {
CallSiteBinder binder = new CallSiteBinder();
Class<?> keyJavaType = Primitives.wrap(keyType.getJavaType());
Class<?> transformedKeyJavaType = Primitives.wrap(transformedKeyType.getJavaType());
Class<?> valueJavaType = Primitives.wrap(valueType.getJavaType());
ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName("MapTransformKey"), type(Object.class));
definition.declareDefaultConstructor(a(PRIVATE));
Parameter state = arg("state", Object.class);
Parameter properties = arg("properties", SqlFunctionProperties.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, properties, 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 typedSet = scope.declareVariable(TypedSet.class, "typeSet");
Variable keyElement = scope.declareVariable(keyJavaType, "keyElement");
Variable transformedKeyElement = scope.declareVariable(transformedKeyJavaType, "transformedKeyElement");
Variable valueElement = scope.declareVariable(valueJavaType, "valueElement");
// 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)));
// create typed set
body.append(typedSet.set(newInstance(TypedSet.class, constantType(binder, transformedKeyType), divide(positionCount, constantInt(2)), constantString(MAP_TRANSFORM_KEY_FUNCTION.getSignature().getNameSuffix()))));
// 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 {
// make sure invokeExact will not take uninitialized keys during compile time
loadValueElement = new BytecodeBlock().append(valueElement.set(constantNull(valueJavaType)));
}
SqlTypeBytecodeExpression transformedKeySqlType = constantType(binder, transformedKeyType);
BytecodeNode writeKeyElement;
BytecodeNode throwDuplicatedKeyException;
if (!transformedKeyType.equals(UNKNOWN)) {
writeKeyElement = new BytecodeBlock().append(transformedKeyElement.set(function.invoke("apply", Object.class, keyElement.cast(Object.class), valueElement.cast(Object.class)).cast(transformedKeyJavaType))).append(new IfStatement().condition(equal(transformedKeyElement, constantNull(transformedKeyJavaType))).ifTrue(throwNullKeyException).ifFalse(new BytecodeBlock().append(constantType(binder, transformedKeyType).writeValue(blockBuilder, transformedKeyElement.cast(transformedKeyType.getJavaType()))).append(valueSqlType.invoke("appendTo", void.class, block, add(position, constantInt(1)), blockBuilder))));
// make sure getObjectValue takes a known key type
throwDuplicatedKeyException = new BytecodeBlock().append(mapBlockBuilder.invoke("closeEntry", BlockBuilder.class).pop()).append(newInstance(PrestoException.class, getStatic(INVALID_FUNCTION_ARGUMENT.getDeclaringClass(), "INVALID_FUNCTION_ARGUMENT").cast(ErrorCodeSupplier.class), invokeStatic(String.class, "format", String.class, constantString("Duplicate keys (%s) are not allowed"), newArray(type(Object[].class), ImmutableList.of(transformedKeySqlType.invoke("getObjectValue", Object.class, properties, blockBuilder.cast(Block.class), position)))))).throwObject();
} else {
// key cannot be unknown
// if we reach this point during runtime, it is an exception
writeKeyElement = throwNullKeyException;
throwDuplicatedKeyException = throwNullKeyException;
}
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(writeKeyElement).append(new IfStatement().condition(typedSet.invoke("contains", boolean.class, blockBuilder.cast(Block.class), position)).ifTrue(throwDuplicatedKeyException).ifFalse(typedSet.invoke("add", boolean.class, blockBuilder.cast(Block.class), position).pop()))));
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(), MapTransformKeyFunction.class.getClassLoader());
return methodHandle(generatedClass, "transform", Object.class, SqlFunctionProperties.class, Block.class, BinaryFunctionInterface.class);
}
use of com.facebook.presto.bytecode.control.ForLoop in project presto by prestodb.
the class PageProcessorCompiler method generateFilterPageMethod.
private void generateFilterPageMethod(ClassDefinition classDefinition, RowExpression filter) {
Parameter session = arg("session", ConnectorSession.class);
Parameter page = arg("page", Page.class);
MethodDefinition method = classDefinition.declareMethod(a(PUBLIC), "filterPage", type(int[].class), session, page);
method.comment("Filter: %s rows in the page", filter.toString());
Scope scope = method.getScope();
Variable thisVariable = method.getThis();
BytecodeBlock body = method.getBody();
Variable positionCount = scope.declareVariable("positionCount", body, page.invoke("getPositionCount", int.class));
Variable selectedPositions = scope.declareVariable("selectedPositions", body, newArray(type(int[].class), positionCount));
Variable selectedCount = scope.declareVariable("selectedCount", body, constantInt(0));
Variable position = scope.declareVariable(int.class, "position");
List<Integer> filterChannels = getInputChannels(filter);
// extract block variables
ImmutableList.Builder<Variable> blockVariablesBuilder = ImmutableList.builder();
for (int channel : filterChannels) {
Variable blockVariable = scope.declareVariable("block_" + channel, body, page.invoke("getBlock", Block.class, constantInt(channel)));
blockVariablesBuilder.add(blockVariable);
}
List<Variable> blockVariables = blockVariablesBuilder.build();
if (filterChannels.size() == 1 && determinismEvaluator.isDeterministic(filter)) {
BytecodeBlock ifFilterOnDictionaryBlock = getBytecodeFilterOnDictionary(session, scope, blockVariables.get(0));
BytecodeBlock ifFilterOnRLEBlock = getBytecodeFilterOnRLE(session, scope, blockVariables.get(0));
body.append(new IfStatement().condition(blockVariables.get(0).instanceOf(DictionaryBlock.class)).ifTrue(ifFilterOnDictionaryBlock));
body.append(new IfStatement().condition(blockVariables.get(0).instanceOf(RunLengthEncodedBlock.class)).ifTrue(ifFilterOnRLEBlock));
}
body.append(new ForLoop().initialize(position.set(constantInt(0))).condition(lessThan(position, positionCount)).update(position.increment()).body(new IfStatement().condition(invokeFilter(thisVariable, session, blockVariables, position)).ifTrue(new BytecodeBlock().append(selectedPositions.setElement(selectedCount, position)).append(selectedCount.increment()))));
body.append(invokeStatic(Arrays.class, "copyOf", int[].class, selectedPositions, selectedCount).ret());
}
use of com.facebook.presto.bytecode.control.ForLoop in project presto by prestodb.
the class PageProcessorCompiler method getBytecodeFilterOnDictionary.
private static BytecodeBlock getBytecodeFilterOnDictionary(Parameter session, Scope scope, Variable blockVariable) {
Variable position = scope.getVariable("position");
Variable positionCount = scope.getVariable("positionCount");
Variable selectedCount = scope.getVariable("selectedCount");
Variable selectedPositions = scope.getVariable("selectedPositions");
Variable thisVariable = scope.getThis();
BytecodeExpression inputFilterDictionary = thisVariable.getField("inputFilterDictionary", Block.class);
BytecodeExpression filterResult = thisVariable.getField("filterResult", boolean[].class);
BytecodeBlock ifFilterOnDictionaryBlock = new BytecodeBlock();
Variable dictionaryBlock = scope.declareVariable("dictionaryBlock", ifFilterOnDictionaryBlock, blockVariable.cast(DictionaryBlock.class));
Variable dictionary = scope.declareVariable("dictionary", ifFilterOnDictionaryBlock, dictionaryBlock.invoke("getDictionary", Block.class));
Variable dictionaryPositionCount = scope.declareVariable("dictionaryPositionCount", ifFilterOnDictionaryBlock, dictionary.invoke("getPositionCount", int.class));
Variable selectedDictionaryPositions = scope.declareVariable("selectedDictionaryPositions", ifFilterOnDictionaryBlock, newArray(type(boolean[].class), dictionaryPositionCount));
// if cached value is available use it else filter dictionary and cache it
ifFilterOnDictionaryBlock.append(new IfStatement().condition(equal(dictionary, inputFilterDictionary)).ifTrue(selectedDictionaryPositions.set(filterResult)).ifFalse(new BytecodeBlock().append(new ForLoop().initialize(position.set(constantInt(0))).condition(lessThan(position, dictionaryPositionCount)).update(position.increment()).body(selectedDictionaryPositions.setElement(position, invokeFilter(thisVariable, session, singletonList(dictionary), position)))).append(thisVariable.setField("inputFilterDictionary", dictionary)).append(thisVariable.setField("filterResult", selectedDictionaryPositions))));
// create selected positions
ifFilterOnDictionaryBlock.append(new ForLoop().initialize(position.set(constantInt(0))).condition(lessThan(position, positionCount)).update(position.increment()).body(new IfStatement().condition(selectedDictionaryPositions.getElement(dictionaryBlock.invoke("getId", int.class, position))).ifTrue(new BytecodeBlock().append(selectedPositions.setElement(selectedCount, position)).append(selectedCount.increment()))));
// return selectedPositions
ifFilterOnDictionaryBlock.append(invokeStatic(Arrays.class, "copyOf", int[].class, selectedPositions, selectedCount).ret());
return ifFilterOnDictionaryBlock;
}
use of com.facebook.presto.bytecode.control.ForLoop in project presto by prestodb.
the class PageProcessorCompiler method generateProcessMethod.
private static void generateProcessMethod(ClassDefinition classDefinition, RowExpression filter, List<RowExpression> projections, List<MethodDefinition> projectionMethods) {
Parameter session = arg("session", ConnectorSession.class);
Parameter page = arg("page", Page.class);
Parameter start = arg("start", int.class);
Parameter end = arg("end", int.class);
Parameter pageBuilder = arg("pageBuilder", PageBuilder.class);
MethodDefinition method = classDefinition.declareMethod(a(PUBLIC), "process", type(int.class), session, page, start, end, pageBuilder);
Scope scope = method.getScope();
BytecodeBlock body = method.getBody();
Variable thisVariable = method.getThis();
// extract blocks
List<Integer> allInputChannels = getInputChannels(concat(projections, ImmutableList.of(filter)));
ImmutableMap.Builder<Integer, Variable> builder = ImmutableMap.builder();
for (int channel : allInputChannels) {
Variable blockVariable = scope.declareVariable("block_" + channel, body, page.invoke("getBlock", Block.class, constantInt(channel)));
builder.put(channel, blockVariable);
}
Map<Integer, Variable> channelBlocks = builder.build();
Map<RowExpression, List<Variable>> expressionInputBlocks = getExpressionInputBlocks(projections, filter, channelBlocks);
// projection body
Variable position = scope.declareVariable(int.class, "position");
BytecodeBlock project = new BytecodeBlock().append(pageBuilder.invoke("declarePosition", void.class));
for (int projectionIndex = 0; projectionIndex < projections.size(); projectionIndex++) {
RowExpression projection = projections.get(projectionIndex);
project.append(invokeProject(thisVariable, session, expressionInputBlocks.get(projection), position, pageBuilder, constantInt(projectionIndex), projectionMethods.get(projectionIndex)));
}
LabelNode done = new LabelNode("done");
// for loop loop body
ForLoop loop = new ForLoop().initialize(position.set(start)).condition(lessThan(position, end)).update(position.set(add(position, constantInt(1)))).body(new BytecodeBlock().append(new IfStatement().condition(pageBuilder.invoke("isFull", boolean.class)).ifTrue(jump(done))).append(new IfStatement().condition(invokeFilter(thisVariable, session, expressionInputBlocks.get(filter), position)).ifTrue(project)));
body.append(loop).visitLabel(done).append(position.ret());
}
use of com.facebook.presto.bytecode.control.ForLoop in project presto by prestodb.
the class PageProcessorCompiler method generateProjectDictionaryMethod.
private MethodDefinition generateProjectDictionaryMethod(ClassDefinition classDefinition, String methodName, RowExpression projection, MethodDefinition project, MethodDefinition projectColumnar, MethodDefinition projectRLE) {
Parameter session = arg("session", ConnectorSession.class);
Parameter page = arg("page", Page.class);
Parameter selectedPositions = arg("selectedPositions", int[].class);
Parameter pageBuilder = arg("pageBuilder", PageBuilder.class);
Parameter projectionIndex = arg("projectionIndex", int.class);
Parameter dictionarySourceIds = arg("dictionarySourceIds", Map.class);
List<Parameter> params = ImmutableList.<Parameter>builder().add(session).add(page).add(selectedPositions).add(pageBuilder).add(projectionIndex).add(dictionarySourceIds).build();
List<Parameter> columnarParams = ImmutableList.<Parameter>builder().add(session).add(page).add(selectedPositions).add(pageBuilder).add(projectionIndex).build();
MethodDefinition method = classDefinition.declareMethod(a(PRIVATE), methodName, type(Block.class), params);
BytecodeBlock body = method.getBody();
Scope scope = method.getScope();
Variable thisVariable = method.getThis();
List<Integer> inputChannels = getInputChannels(projection);
if (inputChannels.size() != 1 || !determinismEvaluator.isDeterministic(projection)) {
body.append(thisVariable.invoke(projectColumnar, columnarParams).ret());
return method;
}
Variable inputBlock = scope.declareVariable("inputBlock", body, page.invoke("getBlock", Block.class, constantInt(getOnlyElement(inputChannels))));
body.append(new IfStatement().condition(inputBlock.instanceOf(RunLengthEncodedBlock.class)).ifTrue(thisVariable.invoke(projectRLE, columnarParams).ret()));
body.append(new IfStatement().condition(inputBlock.instanceOf(DictionaryBlock.class)).ifFalse(thisVariable.invoke(projectColumnar, columnarParams).ret()));
Variable blockBuilder = scope.declareVariable("blockBuilder", body, pageBuilder.invoke("getBlockBuilder", BlockBuilder.class, projectionIndex));
Variable cardinality = scope.declareVariable("cardinality", body, selectedPositions.length());
Variable dictionary = scope.declareVariable(Block.class, "dictionary");
Variable dictionaryCount = scope.declareVariable(int.class, "dictionaryCount");
Variable inputSourceId = scope.declareVariable(DictionaryId.class, "inputSourceId");
Variable outputSourceId = scope.declareVariable(DictionaryId.class, "outputSourceId");
Variable outputDictionary = scope.declareVariable(Block.class, "outputDictionary");
Variable outputIds = scope.declareVariable(int[].class, "outputIds");
BytecodeExpression inputDictionaries = thisVariable.getField("inputDictionaries", Block[].class);
BytecodeExpression outputDictionaries = thisVariable.getField("outputDictionaries", Block[].class);
Variable position = scope.declareVariable("position", body, constantInt(0));
BytecodeExpression castDictionaryBlock = inputBlock.cast(DictionaryBlock.class);
body.comment("Extract dictionary, ids, positionCount and dictionarySourceId").append(dictionary.set(castDictionaryBlock.invoke("getDictionary", Block.class))).append(dictionaryCount.set(dictionary.invoke("getPositionCount", int.class))).append(inputSourceId.set(castDictionaryBlock.invoke("getDictionarySourceId", DictionaryId.class)));
BytecodeBlock projectDictionary = new BytecodeBlock().comment("Project dictionary").append(new ForLoop().initialize(position.set(constantInt(0))).condition(lessThan(position, dictionaryCount)).update(position.increment()).body(invokeProject(thisVariable, session, ImmutableList.of(dictionary), position, pageBuilder, projectionIndex, project))).append(outputDictionary.set(blockBuilder.invoke("build", Block.class))).append(inputDictionaries.setElement(projectionIndex, dictionary)).append(outputDictionaries.setElement(projectionIndex, outputDictionary));
body.comment("Use processed dictionary, if available, else project it").append(new IfStatement().condition(equal(inputDictionaries.getElement(projectionIndex), dictionary)).ifTrue(outputDictionary.set(outputDictionaries.getElement(projectionIndex))).ifFalse(projectDictionary));
body.comment("Filter ids").append(outputIds.set(newArray(type(int[].class), cardinality))).append(new ForLoop().initialize(position.set(constantInt(0))).condition(lessThan(position, cardinality)).update(position.increment()).body(outputIds.setElement(position, castDictionaryBlock.invoke("getId", int.class, selectedPositions.getElement(position)))));
body.append(outputSourceId.set(dictionarySourceIds.invoke("get", Object.class, inputSourceId.cast(Object.class)).cast(DictionaryId.class)));
body.append(new IfStatement().condition(equal(outputSourceId, constantNull(DictionaryId.class))).ifTrue(new BytecodeBlock().append(outputSourceId.set(invokeStatic(DictionaryId.class, "randomDictionaryId", DictionaryId.class))).append(dictionarySourceIds.invoke("put", Object.class, inputSourceId.cast(Object.class), outputSourceId.cast(Object.class))).pop()));
body.append(newInstance(DictionaryBlock.class, cardinality, outputDictionary, outputIds, constantFalse(), outputSourceId).cast(Block.class).ret());
return method;
}
Aggregations