Skip to content

AArch64: Add MatchRules for bit field operations #1181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Arm Limited and affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package org.graalvm.compiler.core.aarch64.test;

import org.graalvm.compiler.lir.LIRInstruction;
import org.graalvm.compiler.lir.aarch64.AArch64BitFieldOp;
import org.junit.Test;

import java.util.function.Predicate;

public class AArch64BitFieldTest extends AArch64MatchRuleTest {
private static final Predicate<LIRInstruction> predicate = op -> (op instanceof AArch64BitFieldOp);

private void testAndCheckLIR(String method, String negativeMethod, Object input) {
test(method, input);
checkLIR(method, predicate, 1);
test(negativeMethod, input);
checkLIR(negativeMethod, predicate, 0);
}

/**
* unsigned bit field extract int.
*/
public static int extractInt(int input) {
return (input >>> 6) & 0xffff;
}

/**
* unsigned bit field extract int (negative cases).
*/
public static int invalidExtractInt(int input) {
int result = 0;
result += ((input >>> 10) & 0xfff0); // Invalid mask
result += ((input >>> 2) & 0x7fffffff); // Bit field width too large
result += ((input >>> 16) & 0x1ffff); // Width + lsb exceeds limit
return result;
}

@Test
public void testExtractInt() {
testAndCheckLIR("extractInt", "invalidExtractInt", 0x12345678);
}

/**
* unsigned bit field extract long.
*/
public static long extractLong(long input) {
return (input >>> 25) & 0xffffffffL;
}

/**
* unsigned bit field extract long (negative cases).
*/
public static long invalidExtractLong(long input) {
long result = 0L;
result += ((input >>> 10) & 0x230L); // Invalid mask
result += ((input >>> 2) & 0x7fffffffffffffffL); // Bit field width too large
result += ((input >>> 62) & 0x7L); // Width + lsb exceeds limit
return result;
}

@Test
public void testExtractLong() {
testAndCheckLIR("extractLong", "invalidExtractLong", 0xfedcba9876543210L);
}

/**
* unsigned bit field insert int.
*/
public static int insertInt(int input) {
return (input & 0xfff) << 10;
}

/**
* unsigned bit field insert int (negative cases).
*/
public static int invalidInsertInt(int input) {
int result = 0;
result += ((input & 0xe) << 25); // Invalid mask
result += ((input & 0x7fffffff) << 1); // Bit field width too large
result += ((input & 0x1ffff) << 16); // Width + lsb exceeds limit
return result;
}

@Test
public void testInsertInt() {
testAndCheckLIR("insertInt", "invalidInsertInt", 0xcafebabe);
}

/**
* unsigned bit field insert long.
*/
public static long insertLong(long input) {
return (input & 0x3fffffffffffL) << 7;
}

/**
* unsigned bit field insert long (negative cases).
*/
public static long invalidInsertLong(long input) {
long result = 0L;
result += ((input & 0x1a) << 39); // Invalid mask
result += ((input & 0x7fffffffffffffffL) << 1); // Bit field width too large
result += ((input & 0x3fffffff) << 52); // Width + lsb exceeds limit
return result;
}

@Test
public void testInsertLong() {
testAndCheckLIR("insertLong", "invalidInsertLong", 0xdeadbeefdeadbeefL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package org.graalvm.compiler.core.aarch64;

import jdk.vm.ci.aarch64.AArch64Kind;
import jdk.vm.ci.code.CodeUtil;
import jdk.vm.ci.meta.AllocatableValue;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.Value;
Expand All @@ -42,6 +43,7 @@
import org.graalvm.compiler.lir.LabelRef;
import org.graalvm.compiler.lir.Variable;
import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp;
import org.graalvm.compiler.lir.aarch64.AArch64BitFieldOp;
import org.graalvm.compiler.lir.aarch64.AArch64ControlFlow;
import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
import org.graalvm.compiler.nodes.ConstantNode;
Expand All @@ -65,7 +67,7 @@

public class AArch64NodeMatchRules extends NodeMatchRules {
private static final EconomicMap<Class<? extends Node>, AArch64ArithmeticOp> nodeOpMap;

private static final EconomicMap<Class<? extends BinaryNode>, AArch64BitFieldOp.BitFieldOpCode> bitFieldOpMap;
private static final EconomicMap<Class<? extends BinaryNode>, AArch64MacroAssembler.ShiftType> shiftTypeMap;

static {
Expand All @@ -76,6 +78,10 @@ public class AArch64NodeMatchRules extends NodeMatchRules {
nodeOpMap.put(OrNode.class, AArch64ArithmeticOp.OR);
nodeOpMap.put(XorNode.class, AArch64ArithmeticOp.XOR);

bitFieldOpMap = EconomicMap.create(Equivalence.IDENTITY, 2);
bitFieldOpMap.put(UnsignedRightShiftNode.class, AArch64BitFieldOp.BitFieldOpCode.UBFX);
bitFieldOpMap.put(LeftShiftNode.class, AArch64BitFieldOp.BitFieldOpCode.UBFIZ);

shiftTypeMap = EconomicMap.create(Equivalence.IDENTITY, 3);
shiftTypeMap.put(LeftShiftNode.class, AArch64MacroAssembler.ShiftType.LSL);
shiftTypeMap.put(RightShiftNode.class, AArch64MacroAssembler.ShiftType.ASR);
Expand All @@ -101,6 +107,19 @@ private AllocatableValue moveSp(AllocatableValue value) {
return getLIRGeneratorTool().moveSp(value);
}

private ComplexMatchResult emitBitField(AArch64BitFieldOp.BitFieldOpCode op, ValueNode value, int lsb, int width) {
assert op != null;
assert value.getStackKind().isNumericInteger();

return builder -> {
Value a = operand(value);
Variable result = gen.newVariable(LIRKind.combine(a));
AllocatableValue src = moveSp(gen.asAllocatable(a));
gen.append(new AArch64BitFieldOp(op, result, src, lsb, width));
return result;
};
}

private ComplexMatchResult emitBinaryShift(AArch64ArithmeticOp op, ValueNode value, BinaryNode shift,
boolean isShiftNot) {
AArch64MacroAssembler.ShiftType shiftType = shiftTypeMap.get(shift.getClass());
Expand Down Expand Up @@ -133,6 +152,40 @@ private ComplexMatchResult emitBitTestAndBranch(FixedNode trueSuccessor, FixedNo
};
}

@MatchRule("(And (UnsignedRightShift=shift a Constant=b) Constant=c)")
@MatchRule("(LeftShift=shift (And a Constant=c) Constant=b)")
public ComplexMatchResult unsignedBitField(BinaryNode shift, ValueNode a, ConstantNode b, ConstantNode c) {
JavaKind srcKind = a.getStackKind();
assert srcKind.isNumericInteger();
AArch64BitFieldOp.BitFieldOpCode op = bitFieldOpMap.get(shift.getClass());
assert op != null;
int distance = b.asJavaConstant().asInt();
long mask = c.asJavaConstant().asLong();

// The Java(R) Language Specification CHAPTER 15.19 Shift Operators says:
// "If the promoted type of the left-hand operand is int(long), then only the five(six)
// lowest-order bits of the right-hand operand are used as the shift distance."
distance = distance & (srcKind == JavaKind.Int ? 0x1f : 0x3f);

// Constraint 1: Mask plus one should be a power-of-2 integer.
if (!CodeUtil.isPowerOf2(mask + 1)) {
return null;
}
int width = CodeUtil.log2(mask + 1);
int srcBits = srcKind.getBitCount();
// Constraint 2: Bit field width is less than 31(63) for int(long) as any bit field move
// operations can be done by a single shift instruction if the width is 31(63).
if (width >= srcBits - 1) {
return null;
}
// Constraint 3: Sum of bit field width and the shift distance is less or equal to 32(64)
// for int(long) as the specification of AArch64 bit field instructions.
if (width + distance > srcBits) {
return null;
}
return emitBitField(op, a, distance, width);
}

@MatchRule("(Add=binary a (LeftShift=shift b Constant))")
@MatchRule("(Add=binary a (RightShift=shift b Constant))")
@MatchRule("(Add=binary a (UnsignedRightShift=shift b Constant))")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Arm Limited and affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package org.graalvm.compiler.lir.aarch64;

import jdk.vm.ci.code.Register;
import jdk.vm.ci.meta.AllocatableValue;
import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.lir.LIRInstructionClass;
import org.graalvm.compiler.lir.Opcode;
import org.graalvm.compiler.lir.asm.CompilationResultBuilder;

import static jdk.vm.ci.code.ValueUtil.asRegister;
import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;

/**
* Bit field ops for AArch64.
*/
public class AArch64BitFieldOp extends AArch64LIRInstruction {
public enum BitFieldOpCode {
UBFX,
UBFIZ,
}

private static final LIRInstructionClass<AArch64BitFieldOp> TYPE = LIRInstructionClass.create(AArch64BitFieldOp.class);

@Opcode private final AArch64BitFieldOp.BitFieldOpCode opcode;
@Def protected AllocatableValue result;
@Use({REG}) protected AllocatableValue input;
private final int lsb;
private final int width;

public AArch64BitFieldOp(AArch64BitFieldOp.BitFieldOpCode opcode, AllocatableValue result,
AllocatableValue input, int lsb, int width) {
super(TYPE);
this.opcode = opcode;
this.result = result;
this.input = input;
this.lsb = lsb;
this.width = width;
}

@Override
protected void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) {
Register dst = asRegister(result);
Register src = asRegister(input);
final int size = input.getPlatformKind().getSizeInBytes() * Byte.SIZE;
switch (opcode) {
case UBFX:
masm.ubfm(size, dst, src, lsb, lsb + width - 1);
break;
case UBFIZ:
masm.ubfm(size, dst, src, size - lsb, width - 1);
break;
default:
throw GraalError.shouldNotReachHere();
}
}
}