Search in sources :

Example 21 with BytecodeNode

use of com.facebook.presto.bytecode.BytecodeNode in project presto by prestodb.

the class AccumulatorCompiler method generateInputForLoop.

private static BytecodeBlock generateInputForLoop(List<FieldDefinition> stateField, List<ParameterMetadata> parameterMetadatas, MethodHandle inputFunction, Scope scope, List<Variable> parameterVariables, List<Class> lambdaInterfaces, List<FieldDefinition> lambdaProviderFields, Variable masksBlock, CallSiteBinder callSiteBinder, boolean grouped) {
    // For-loop over rows
    Variable page = scope.getVariable("page");
    Variable positionVariable = scope.declareVariable(int.class, "position");
    Variable rowsVariable = scope.declareVariable(int.class, "rows");
    BytecodeBlock block = new BytecodeBlock().append(page).invokeVirtual(Page.class, "getPositionCount", int.class).putVariable(rowsVariable).initializeVariable(positionVariable);
    BytecodeNode loopBody = generateInvokeInputFunction(scope, stateField, positionVariable, parameterVariables, parameterMetadatas, lambdaInterfaces, lambdaProviderFields, inputFunction, callSiteBinder, grouped);
    // Wrap with null checks
    List<Boolean> nullable = new ArrayList<>();
    for (ParameterMetadata metadata : parameterMetadatas) {
        switch(metadata.getParameterType()) {
            case INPUT_CHANNEL:
            case BLOCK_INPUT_CHANNEL:
                nullable.add(false);
                break;
            case NULLABLE_BLOCK_INPUT_CHANNEL:
                nullable.add(true);
                break;
            // do nothing
            default:
        }
    }
    checkState(nullable.size() == parameterVariables.size(), "Number of parameters does not match");
    for (int i = 0; i < parameterVariables.size(); i++) {
        if (!nullable.get(i)) {
            Variable variableDefinition = parameterVariables.get(i);
            loopBody = new IfStatement("if(!%s.isNull(position))", variableDefinition.getName()).condition(new BytecodeBlock().getVariable(variableDefinition).getVariable(positionVariable).invokeInterface(Block.class, "isNull", boolean.class, int.class)).ifFalse(loopBody);
        }
    }
    loopBody = new IfStatement("if(testMask(%s, position))", masksBlock.getName()).condition(new BytecodeBlock().getVariable(masksBlock).getVariable(positionVariable).invokeStatic(CompilerOperations.class, "testMask", boolean.class, Block.class, int.class)).ifTrue(loopBody);
    BytecodeNode forLoop = new ForLoop().initialize(new BytecodeBlock().putVariable(positionVariable, 0)).condition(new BytecodeBlock().getVariable(positionVariable).getVariable(rowsVariable).invokeStatic(CompilerOperations.class, "lessThan", boolean.class, int.class, int.class)).update(new BytecodeBlock().incrementVariable(positionVariable, (byte) 1)).body(loopBody);
    for (int i = 0; i < parameterVariables.size(); i++) {
        if (!nullable.get(i)) {
            Variable variableDefinition = parameterVariables.get(i);
            forLoop = new IfStatement("if(!(%s instanceof RunLengthEncodedBlock && %s.isNull(0)))", variableDefinition.getName(), variableDefinition.getName()).condition(and(variableDefinition.instanceOf(RunLengthEncodedBlock.class), variableDefinition.invoke("isNull", boolean.class, constantInt(0)))).ifFalse(forLoop);
        }
    }
    // Skip input blocks that eliminate all input positions
    forLoop = new IfStatement("if(!(%s instanceof RunLengthEncodedBlock && !testMask(%s, 0)))", masksBlock.getName(), masksBlock.getName()).condition(and(masksBlock.instanceOf(RunLengthEncodedBlock.class), not(invokeStatic(type(CompilerOperations.class), "testMask", type(boolean.class), ImmutableList.of(type(Block.class), type(int.class)), masksBlock, constantInt(0))))).ifFalse(forLoop);
    block.append(new IfStatement("if(%s > 0)", rowsVariable.getName()).condition(new BytecodeBlock().getVariable(rowsVariable).push(0).invokeStatic(CompilerOperations.class, "greaterThan", boolean.class, int.class, int.class)).ifTrue(forLoop));
    return block;
}
Also used : IfStatement(com.facebook.presto.bytecode.control.IfStatement) Variable(com.facebook.presto.bytecode.Variable) ForLoop(com.facebook.presto.bytecode.control.ForLoop) CompilerOperations(com.facebook.presto.sql.gen.CompilerOperations) BytecodeBlock(com.facebook.presto.bytecode.BytecodeBlock) ArrayList(java.util.ArrayList) RunLengthEncodedBlock(com.facebook.presto.common.block.RunLengthEncodedBlock) GroupByIdBlock(com.facebook.presto.operator.GroupByIdBlock) BytecodeBlock(com.facebook.presto.bytecode.BytecodeBlock) Block(com.facebook.presto.common.block.Block) BytecodeNode(com.facebook.presto.bytecode.BytecodeNode) ParameterMetadata(com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata)

Example 22 with BytecodeNode

use of com.facebook.presto.bytecode.BytecodeNode in project presto by prestodb.

the class AbstractMinMaxBy method generateInputMethod.

private void generateInputMethod(ClassDefinition definition, CallSiteBinder binder, MethodHandle compareMethod, Type keyType, Type valueType, Class<?> stateClass) {
    Parameter state = arg("state", stateClass);
    Parameter value = arg("value", Block.class);
    Parameter key = arg("key", Block.class);
    Parameter position = arg("position", int.class);
    MethodDefinition method = definition.declareMethod(a(PUBLIC, STATIC), "input", type(void.class), state, value, key, position);
    SqlTypeBytecodeExpression keySqlType = constantType(binder, keyType);
    BytecodeBlock ifBlock = new BytecodeBlock().append(state.invoke("setFirst", void.class, keySqlType.getValue(key, position))).append(state.invoke("setFirstNull", void.class, constantBoolean(false))).append(state.invoke("setSecondNull", void.class, value.invoke("isNull", boolean.class, position)));
    BytecodeNode setValueNode;
    if (valueType.getJavaType().isPrimitive()) {
        SqlTypeBytecodeExpression valueSqlType = constantType(binder, valueType);
        setValueNode = state.invoke("setSecond", void.class, valueSqlType.getValue(value, position));
    } else {
        // Do not get value directly given it creates object overhead.
        // Such objects would live long enough in Block or SliceBigArray to cause GC pressure.
        setValueNode = new BytecodeBlock().append(state.invoke("setSecondBlock", void.class, value)).append(state.invoke("setSecondPosition", void.class, position));
    }
    ifBlock.append(new IfStatement().condition(value.invoke("isNull", boolean.class, position)).ifFalse(setValueNode));
    method.getBody().append(new IfStatement().condition(or(state.invoke("isFirstNull", boolean.class), and(not(key.invoke("isNull", boolean.class, position)), loadConstant(binder, compareMethod, MethodHandle.class).invoke("invokeExact", boolean.class, keySqlType.getValue(key, position), state.invoke("getFirst", keyType.getJavaType()))))).ifTrue(ifBlock)).ret();
}
Also used : IfStatement(com.facebook.presto.bytecode.control.IfStatement) MethodDefinition(com.facebook.presto.bytecode.MethodDefinition) BytecodeBlock(com.facebook.presto.bytecode.BytecodeBlock) Signature.orderableTypeParameter(com.facebook.presto.spi.function.Signature.orderableTypeParameter) Parameter(com.facebook.presto.bytecode.Parameter) BytecodeNode(com.facebook.presto.bytecode.BytecodeNode) SqlTypeBytecodeExpression(com.facebook.presto.sql.gen.SqlTypeBytecodeExpression) MethodHandle(java.lang.invoke.MethodHandle)

Example 23 with BytecodeNode

use of com.facebook.presto.bytecode.BytecodeNode 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);
}
Also used : Signature.typeVariable(com.facebook.presto.spi.function.Signature.typeVariable) Variable(com.facebook.presto.bytecode.Variable) VariableInstruction.incrementVariable(com.facebook.presto.bytecode.instruction.VariableInstruction.incrementVariable) ErrorCodeSupplier(com.facebook.presto.spi.ErrorCodeSupplier) ForLoop(com.facebook.presto.bytecode.control.ForLoop) BytecodeBlock(com.facebook.presto.bytecode.BytecodeBlock) PrestoException(com.facebook.presto.spi.PrestoException) ClassDefinition(com.facebook.presto.bytecode.ClassDefinition) IfStatement(com.facebook.presto.bytecode.control.IfStatement) Scope(com.facebook.presto.bytecode.Scope) MethodDefinition(com.facebook.presto.bytecode.MethodDefinition) CallSiteBinder(com.facebook.presto.bytecode.CallSiteBinder) Parameter(com.facebook.presto.bytecode.Parameter) TypeSignatureParameter(com.facebook.presto.common.type.TypeSignatureParameter) BytecodeBlock(com.facebook.presto.bytecode.BytecodeBlock) Block(com.facebook.presto.common.block.Block) BytecodeNode(com.facebook.presto.bytecode.BytecodeNode) SqlTypeBytecodeExpression(com.facebook.presto.sql.gen.SqlTypeBytecodeExpression) BlockBuilder(com.facebook.presto.common.block.BlockBuilder)

Example 24 with BytecodeNode

use of com.facebook.presto.bytecode.BytecodeNode in project presto by prestodb.

the class SwitchCodeGenerator method generateExpression.

@Override
public BytecodeNode generateExpression(Signature signature, BytecodeGeneratorContext generatorContext, Type returnType, List<RowExpression> arguments) {
    // TODO: compile as
    /*
            hashCode = hashCode(<value>)

            // all constant expressions before a non-constant
            switch (hashCode) {
                case ...:
                    if (<value> == <constant1>) {
                       ...
                    }
                    else if (<value> == <constant2>) {
                       ...
                    }
                    else if (...) {
                    }
                case ...:
                    ...
            }

            if (<value> == <non-constant1>) {
                ...
            }
            else if (<value> == <non-constant2>) {
                ...
            }
            ...

            // repeat with next sequence of constant expressions
         */
    Scope scope = generatorContext.getScope();
    // process value, else, and all when clauses
    RowExpression value = arguments.get(0);
    BytecodeNode valueBytecode = generatorContext.generate(value);
    BytecodeNode elseValue;
    List<RowExpression> whenClauses;
    RowExpression last = arguments.get(arguments.size() - 1);
    if (last instanceof CallExpression && ((CallExpression) last).getSignature().getName().equals("WHEN")) {
        whenClauses = arguments.subList(1, arguments.size());
        elseValue = new BytecodeBlock().append(generatorContext.wasNull().set(constantTrue())).pushJavaDefault(returnType.getJavaType());
    } else {
        whenClauses = arguments.subList(1, arguments.size() - 1);
        elseValue = generatorContext.generate(last);
    }
    // determine the type of the value and result
    Class<?> valueType = value.getType().getJavaType();
    // evaluate the value and store it in a variable
    LabelNode nullValue = new LabelNode("nullCondition");
    Variable tempVariable = scope.createTempVariable(valueType);
    BytecodeBlock block = new BytecodeBlock().append(valueBytecode).append(BytecodeUtils.ifWasNullClearPopAndGoto(scope, nullValue, void.class, valueType)).putVariable(tempVariable);
    BytecodeNode getTempVariableNode = VariableInstruction.loadVariable(tempVariable);
    // build the statements
    elseValue = new BytecodeBlock().visitLabel(nullValue).append(elseValue);
    // reverse list because current if statement builder doesn't support if/else so we need to build the if statements bottom up
    for (RowExpression clause : Lists.reverse(whenClauses)) {
        Preconditions.checkArgument(clause instanceof CallExpression && ((CallExpression) clause).getSignature().getName().equals("WHEN"));
        RowExpression operand = ((CallExpression) clause).getArguments().get(0);
        RowExpression result = ((CallExpression) clause).getArguments().get(1);
        // call equals(value, operand)
        Signature equalsFunction = generatorContext.getRegistry().resolveOperator(OperatorType.EQUAL, ImmutableList.of(value.getType(), operand.getType()));
        // TODO: what if operand is null? It seems that the call will return "null" (which is cleared below)
        // and the code only does the right thing because the value in the stack for that scenario is
        // Java's default for boolean == false
        // This code should probably be checking for wasNull after the call and "failing" the equality
        // check if wasNull is true
        BytecodeNode equalsCall = generatorContext.generateCall(equalsFunction.getName(), generatorContext.getRegistry().getScalarFunctionImplementation(equalsFunction), ImmutableList.of(generatorContext.generate(operand), getTempVariableNode));
        BytecodeBlock condition = new BytecodeBlock().append(equalsCall).append(generatorContext.wasNull().set(constantFalse()));
        elseValue = new IfStatement("when").condition(condition).ifTrue(generatorContext.generate(result)).ifFalse(elseValue);
    }
    return block.append(elseValue);
}
Also used : LabelNode(com.facebook.presto.bytecode.instruction.LabelNode) IfStatement(com.facebook.presto.bytecode.control.IfStatement) Variable(com.facebook.presto.bytecode.Variable) Scope(com.facebook.presto.bytecode.Scope) Signature(com.facebook.presto.metadata.Signature) BytecodeBlock(com.facebook.presto.bytecode.BytecodeBlock) RowExpression(com.facebook.presto.sql.relational.RowExpression) BytecodeNode(com.facebook.presto.bytecode.BytecodeNode) CallExpression(com.facebook.presto.sql.relational.CallExpression)

Example 25 with BytecodeNode

use of com.facebook.presto.bytecode.BytecodeNode in project presto by prestodb.

the class LambdaBytecodeGenerator method defineLambdaMethodAndField.

private static FieldDefinition defineLambdaMethodAndField(BytecodeExpressionVisitor innerExpressionVisitor, ClassDefinition classDefinition, String fieldAndMethodName, List<Parameter> inputParameters, LambdaDefinitionExpression lambda) {
    Class<?> returnType = Primitives.wrap(lambda.getBody().getType().getJavaType());
    MethodDefinition method = classDefinition.declareMethod(a(PUBLIC), fieldAndMethodName, type(returnType), inputParameters);
    Scope scope = method.getScope();
    Variable wasNull = scope.declareVariable(boolean.class, "wasNull");
    BytecodeNode compiledBody = lambda.getBody().accept(innerExpressionVisitor, scope);
    method.getBody().putVariable(wasNull, false).append(compiledBody).append(boxPrimitiveIfNecessary(scope, returnType)).ret(returnType);
    FieldDefinition methodHandleField = classDefinition.declareField(a(PRIVATE, STATIC, FINAL), fieldAndMethodName, type(MethodHandle.class));
    classDefinition.getClassInitializer().getBody().append(setStatic(methodHandleField, invokeStatic(Reflection.class, "methodHandle", MethodHandle.class, constantClass(classDefinition.getType()), constantString(fieldAndMethodName), newArray(type(Class[].class), inputParameters.stream().map(Parameter::getType).map(BytecodeExpressions::constantClass).collect(toImmutableList())))));
    return methodHandleField;
}
Also used : Variable(com.facebook.presto.bytecode.Variable) Scope(com.facebook.presto.bytecode.Scope) MethodDefinition(com.facebook.presto.bytecode.MethodDefinition) FieldDefinition(com.facebook.presto.bytecode.FieldDefinition) Parameter(com.facebook.presto.bytecode.Parameter) BytecodeNode(com.facebook.presto.bytecode.BytecodeNode) BytecodeExpressions.constantClass(com.facebook.presto.bytecode.expression.BytecodeExpressions.constantClass) MethodHandle(java.lang.invoke.MethodHandle)

Aggregations

BytecodeNode (com.facebook.presto.bytecode.BytecodeNode)42 Variable (com.facebook.presto.bytecode.Variable)33 BytecodeBlock (com.facebook.presto.bytecode.BytecodeBlock)29 IfStatement (com.facebook.presto.bytecode.control.IfStatement)23 Scope (com.facebook.presto.bytecode.Scope)21 MethodDefinition (com.facebook.presto.bytecode.MethodDefinition)19 Parameter (com.facebook.presto.bytecode.Parameter)16 LabelNode (com.facebook.presto.bytecode.instruction.LabelNode)12 ClassDefinition (com.facebook.presto.bytecode.ClassDefinition)7 ForLoop (com.facebook.presto.bytecode.control.ForLoop)7 RowExpression (com.facebook.presto.sql.relational.RowExpression)7 ArrayList (java.util.ArrayList)6 FieldDefinition (com.facebook.presto.bytecode.FieldDefinition)5 BytecodeExpression (com.facebook.presto.bytecode.expression.BytecodeExpression)5 Block (com.facebook.presto.common.block.Block)5 Type (com.facebook.presto.common.type.Type)5 FunctionHandle (com.facebook.presto.spi.function.FunctionHandle)5 RowExpression (com.facebook.presto.spi.relation.RowExpression)5 SqlTypeBytecodeExpression (com.facebook.presto.sql.gen.SqlTypeBytecodeExpression)5 ImmutableList (com.google.common.collect.ImmutableList)5