Skip to content
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

feature: SequenceLayout support #111

Merged
merged 1 commit into from
Jul 7, 2024
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
6 changes: 4 additions & 2 deletions src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
import de.sirywell.handlehints.foreign.MemoryLayoutHelper
import de.sirywell.handlehints.mhtype.*
import de.sirywell.handlehints.type.*
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf

class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData) {
companion object {
Expand Down Expand Up @@ -51,7 +49,7 @@
return
}
val value = ssaConstruction.readVariable(instruction.variable, block)
if (value is Holder) {

Check notice on line 52 in src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Cascade 'if' can be replaced with 'when'

Cascade 'if' should be replaced with 'when'
typeData[element] = value.value
} else if (value is Phi) {
val type = value.blockToValue.values
Expand Down Expand Up @@ -317,6 +315,10 @@
}
"structLayout" -> memoryLayoutHelper.structLayout(arguments, block)
"unionLayout" -> memoryLayoutHelper.unionLayout(arguments, block)
"sequenceLayout" -> {
if (arguments.size != 2) return noMatch()
memoryLayoutHelper.sequenceLayout(arguments[0], arguments[1], block)
}
"paddingLayout" -> {
if (arguments.size != 1) return noMatch()
memoryLayoutHelper.paddingLayout(arguments[0])
Expand Down Expand Up @@ -511,7 +513,7 @@

"filterReturnValue" -> {
if (arguments.size != 1) return noMatch()
methodHandlesMerger.filterReturnValue(arguments[0], arguments[1], block)

Check warning on line 516 in src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Constant conditions

Index is always out of bounds
}

"foldArguments" -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,46 @@ class MemoryLayoutHelper(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(
return t % ba
}

fun sequenceLayout(
elementCountExpr: PsiExpression,
elementLayoutExpr: PsiExpression,
block: SsaConstruction.Block
): MemoryLayoutType {
val elementCount = elementCountExpr.asLong()
val elementLayout = ssaAnalyzer.memoryLayoutType(elementLayoutExpr, block) ?: TopMemoryLayoutType
if (elementCount != null) {
if (elementCount < 0) {
return emitProblem(
elementCountExpr,
message("problem.general.argument.numericConditionMismatch", ">= 0", elementCount)
)
}

// 0 is a sane fallback for this check as it is conservative
try {
Math.multiplyExact(elementLayout.byteSize ?: 0, elementCount)
} catch (_: ArithmeticException) {
return emitProblem(
elementCountExpr,
message("problem.foreign.memory.layoutSizeOverflow")
)

}
}
// if one is null, the result is 0
if ((elementLayout.byteSize ?: 0) % (elementLayout.byteAlignment ?: 1) != 0L) {
return emitProblem(
elementLayoutExpr,
message(
"problem.foreign.memory.alignmentMismatch",
elementLayout.byteSize!!,
elementLayout.byteAlignment!!
)
)
}
return SequenceLayoutType(elementLayout, elementCount, elementLayout.byteAlignment)
}

fun paddingLayout(byteSizeExpr: PsiExpression): MemoryLayoutType {
val size = byteSizeExpr.asLong() ?: return PaddingLayoutType(1, null)
if (size <= 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ class TypePrinter : TypeVisitor<TypePrinter.PrintContext, Unit> {
type.memberLayouts.accept(this, context.copy(memoryLayoutSeparator = "|"))
}

override fun visit(type: SequenceLayoutType, context: PrintContext) {
context.append("[")
context.append(type.elementCount ?: "?").append(":")
type.elementLayout.accept(this, context)
context.append("]")
}

override fun visit(type: PaddingLayoutType, context: PrintContext) {
context.append("x")
context.append(type.byteSize ?: "?")
Expand Down
38 changes: 38 additions & 0 deletions src/main/kotlin/de/sirywell/handlehints/type/MemoryLayoutType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,35 @@ data class UnionLayoutType(
override fun <C, R> accept(visitor: TypeVisitor<C, R>, context: C) = visitor.visit(this, context)
}

data class SequenceLayoutType(
val elementLayout: MemoryLayoutType,
val elementCount: Long?,
override val byteAlignment: Long?
) : MemoryLayoutType {
override fun withByteAlignment(byteAlignment: Long): MemoryLayoutType {
return SequenceLayoutType(this.elementLayout, this.elementCount, byteAlignment)
}

override val byteSize = elementCount?.let { elementLayout.byteSize?.times(it) }

override fun joinIdentical(other: MemoryLayoutType): Pair<MemoryLayoutType, TriState> {
if (other is BotMemoryLayoutType) return this to TriState.UNKNOWN
if (other !is SequenceLayoutType) return TopMemoryLayoutType to TriState.UNKNOWN
val (element, identical) = this.elementLayout.joinIdentical(other.elementLayout)
val (identicalAlignment, identicalSize) = joinElementCountAndAlignment(this, other)
return SequenceLayoutType(
element,
if (identicalSize == TriState.YES) this.byteSize else null,
if (identicalAlignment == TriState.YES) this.byteAlignment else null
) to identical.sharpenTowardsNo(identicalAlignment).sharpenTowardsNo(identicalSize)
}

override fun <C, R> accept(visitor: TypeVisitor<C, R>, context: C): R {
return visitor.visit(this, context)
}

}

data class PaddingLayoutType(
override val byteAlignment: Long?,
override val byteSize: Long?
Expand Down Expand Up @@ -136,6 +165,15 @@ private fun joinSizeAndAlignment(first: MemoryLayoutType, second: MemoryLayoutTy
return identicalAlignment to identicalSize
}

private fun joinElementCountAndAlignment(
first: SequenceLayoutType,
second: SequenceLayoutType
): Pair<TriState, TriState> {
val identicalAlignment = (first.byteAlignment?.equals(second.byteAlignment)).toTriState()
val identicalElementCount = (first.elementCount?.equals(second.elementCount)).toTriState()
return identicalAlignment to identicalElementCount
}

typealias MemoryLayoutList = TypeLatticeElementList<MemoryLayoutType>

data object TopMemoryLayoutList : TopTypeLatticeElementList<MemoryLayoutType>() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface TypeVisitor<C, R> {
fun visit(type: ValueLayoutType, context: C): R
fun visit(type: StructLayoutType, context: C): R
fun visit(type: UnionLayoutType, context: C): R
fun visit(type: SequenceLayoutType, context: C): R
fun visit(type: PaddingLayoutType, context: C): R
fun visit(type: TopMemoryLayoutList, context: C): R
fun visit(type: BotMemoryLayoutList, context: C): R
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/messages/MethodHandleMessages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ problem.merging.tableSwitch.notIdentical=The passed MethodHandle type is not com
problem.invocation.returnType.mustBeVoid=MethodHandle returns void but is called in a non-void context.
problem.general.argument.notAPowerOfTwo=Argument must be a power of 2 but was {0}.
problem.general.argument.numericConditionMismatch=Argument must be {0} but is {1}.
problem.foreign.memory.layoutSizeOverflow=Resulting layout byte size overflows.
problem.foreign.memory.layoutMismatch=Layout requires a byte alignment of {0} but is inserted at an offset of {1}.
problem.foreign.memory.alignmentMismatch=Layout must be {0} byte aligned but is {1} byte aligned.
problem.foreign.memory.layoutMismatch.adjustPadding=Adjust the padding before this layout
problem.foreign.memory.layoutMismatch.adjustAlignment=Adjust the alignment of this layout
problem.foreign.memory.invalidAlignment=This layout cannot be {0} byte aligned due to one of its member layouts.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class MemoryLayoutTypeTest : TypeAnalysisTestBase() {

fun testMemoryLayoutUnionLayout() = doTypeCheckingTest()

fun testMemoryLayoutSequenceLayout() = doTypeCheckingTest()


fun testMemoryLayoutStructLayoutInspection() = doInspectionTest()
}
21 changes: 21 additions & 0 deletions src/test/testData/MemoryLayoutSequenceLayout.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.StructLayout;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.ValueLayout;

class MemoryLayoutSequenceLayout {
void m() {
// 0 is allowed
<info descr="[0:int4]">SequenceLayout sl0 = <info descr="[0:int4]">MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT)</info>;</info>
// larger counts are allowed
<info descr="[10:int4]">SequenceLayout sl1 = <info descr="[10:int4]">MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT)</info>;</info>
// negative counts are not allowed
<info descr="⊤">SequenceLayout sl2 = <info descr="⊤">MemoryLayout.sequenceLayout(-10, ValueLayout.JAVA_INT)</info>;</info>
// overflows are not allowed
<info descr="⊤">SequenceLayout sl3 = <info descr="⊤">MemoryLayout.sequenceLayout(Long.MAX_VALUE >> 1, ValueLayout.JAVA_LONG)</info>;</info>
// alignment mismatches are not allowed
<info descr="⊤">SequenceLayout sl4 = <info descr="⊤">MemoryLayout.sequenceLayout(1, <info descr="8%[long8int4]">MemoryLayout.structLayout(ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT)</info>)</info>;</info>
// but matching alignment is fine
<info descr="[1:8%[long8x4int4]]">SequenceLayout sl5 = <info descr="[1:8%[long8x4int4]]">MemoryLayout.sequenceLayout(1, <info descr="8%[long8x4int4]">MemoryLayout.structLayout(ValueLayout.JAVA_LONG, <info descr="x4">MemoryLayout.paddingLayout(4)</info>, ValueLayout.JAVA_INT)</info>)</info>;</info>
}
}
Loading