Skip to content
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
68 changes: 34 additions & 34 deletions bytecodeAudit/package.mill
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@ import java.io.File
/** Tier 1 of https://github.com/Quafadas/vecxt/issues/105 — static bytecode analysis.
*
* Phase 0 landed C6a as a munit test in `experiments.test`, deliberately ahead of any infrastructure. Phase 1 is that
* infrastructure: HotSpot threshold discovery (§1.0), the `@HotPath`/`@Thin`/`@AllocFree` annotations, checks C1, C2, C3
* and C9, a checked-in baseline, and a report. C6a moves here with them, so that the exclusion list, the report and the
* checks are one thing rather than three.
* infrastructure: HotSpot threshold discovery (§1.0), the `@HotPath`/`@Thin`/`@AllocFree` annotations, checks C1, C2,
* C3 and C9, a checked-in baseline, and a report. C6a moves here with them, so that the exclusion list, the report and
* the checks are one thing rather than three.
*
* The checks run as munit tests rather than as a Mill task, which is worth explaining because the plan describes a task.
* Two reasons, and the second is the load-bearing one:
* The checks run as munit tests rather than as a Mill task, which is worth explaining because the plan describes a
* task. Two reasons, and the second is the load-bearing one:
*
* - The analyzer is ordinary module code, so the checks and their canary fixtures call exactly the same functions. An
* analyzer living in a build file can only be exercised by running the build, which is the reason the plan's own
* acceptance criteria ask for canaries that are *verified* to fail.
* - A failing test produces a JUnit report, which CI turns into check-run annotations against the diff. A failing Mill
* task produces console output. When a finding has to travel from CI back to whoever is reading the PR, the first is
* the channel that works.
* - The analyzer is ordinary module code, so the checks and their canary fixtures call exactly the same functions.
* An analyzer living in a build file can only be exercised by running the build, which is the reason the plan's
* own acceptance criteria ask for canaries that are *verified* to fail.
* - A failing test produces a JUnit report, which CI turns into check-run annotations against the diff. A failing
* Mill task produces console output. When a finding has to travel from CI back to whoever is reading the PR, the
* first is the channel that works.
*
* The report is still written to a fixed directory on every run (pass or fail) for CI to upload and to post as a sticky
* PR comment, so the task-shaped outputs the plan asks for all exist — see `reportDir` below.
* The report is still written to a fixed directory on every run (pass or fail) for CI to upload and to post as a
* sticky PR comment, so the task-shaped outputs the plan asks for all exist — see `reportDir` below.
*
* ./mill bytecodeAudit.test # run every check, write out/bytecode-audit/
* ./mill bytecodeAudit.updateBaseline # adopt the proposal the last run wrote
* ./mill bytecodeAudit.test # run every check, write out/bytecode-audit/ ./mill bytecodeAudit.updateBaseline # adopt
* the proposal the last run wrote
*/
object `package` extends ScalaModule:

Expand All @@ -38,14 +38,14 @@ object `package` extends ScalaModule:
)

/** Where the report lands. Not `Task.dest`: CI has to find it without asking Mill, `updateBaseline` has to find the
* proposal a *failed* run wrote, and neither works from a task-scoped directory. Deliberately not `out/bytecodeAudit`,
* which is Mill's own output directory for this module.
* proposal a *failed* run wrote, and neither works from a task-scoped directory. Deliberately not
* `out/bytecodeAudit`, which is Mill's own output directory for this module.
*
* Takes the workspace root rather than reading `os.pwd`. The first version used `os.pwd`, which is not the workspace
* root inside a task — the build evaluates in a server process — so the report was written somewhere consistent but
* wrong. The suite's own "the report was written" assertion passed against that path while CI's artifact upload and PR
* comment found nothing, which is the failure mode to avoid: an audit that reports on itself successfully and publishes
* nothing.
* wrong. The suite's own "the report was written" assertion passed against that path while CI's artifact upload and
* PR comment found nothing, which is the failure mode to avoid: an audit that reports on itself successfully and
* publishes nothing.
*/
def reportDir(ws: os.Path): os.Path = ws / "out" / "bytecode-audit"

Expand All @@ -57,10 +57,10 @@ object `package` extends ScalaModule:
* guess at the API a third time, this uses `sources()`, which this file already calls and which resolves to
* `<workspace>/bytecodeAudit/src`.
*
* The `require` is the point. The previous two attempts were both wrong *silently*: the report went to a path that was
* consistent enough for the suite's own "was it written" assertion to pass, while CI's upload and PR comment found
* nothing. A derivation that cannot be checked is how that happens twice, so this one asserts it landed somewhere with a
* `build.mill` in it.
* The `require` is the point. The previous two attempts were both wrong *silently*: the report went to a path that
* was consistent enough for the suite's own "was it written" assertion to pass, while CI's upload and PR comment
* found nothing. A derivation that cannot be checked is how that happens twice, so this one asserts it landed
* somewhere with a `build.mill` in it.
*/
private def workspaceOf(sourceDirs: Seq[os.Path]): os.Path =
val src = sourceDirs
Expand All @@ -74,23 +74,23 @@ object `package` extends ScalaModule:
/** Adopts the baseline proposal from the last audit run.
*
* Deliberately does not depend on the audit task. The audit FAILs when the baseline is missing or the ratchet has
* moved, and a command that ran the audit first could never be used to fix that — Mill would refuse to run it. So the
* audit always writes its proposal, and this copies it.
* moved, and a command that ran the audit first could never be used to fix that — Mill would refuse to run it. So
* the audit always writes its proposal, and this copies it.
*/
def updateBaseline() = Task.Command {
val ws = workspaceOf(sources().map(_.path))
val target = baselineFile(ws)
val proposed = reportDir(ws) / "baseline-proposed.json"
if !os.exists(proposed) then
sys.error(s"$proposed does not exist — run ./mill bytecodeAudit.test first, then this")
if !os.exists(proposed) then sys.error(s"$proposed does not exist — run ./mill bytecodeAudit.test first, then this")
end if
os.makeDir.all(target / os.up)
os.copy.over(proposed, target)
Task.log.info(s"baseline adopted from $proposed -> $target")
PathRef(target)
}

/** Deliberately-broken fixtures. Compiled, never audited: the whole point is that every check below FAILs on them, and
* a check that has never been observed to fail might not work.
/** Deliberately-broken fixtures. Compiled, never audited: the whole point is that every check below FAILs on them,
* and a check that has never been observed to fail might not work.
*
* Not on any audited root, so nothing here can leak into a production finding.
*/
Expand All @@ -104,8 +104,8 @@ object `package` extends ScalaModule:
*
* That sweep is here rather than in `experiments` because it is the other half of C6a, not a separate concern. C6a
* cannot see a generic *allocation*: `new Array[A](n)` resolves through `ClassTag[A]`, and whether the result is a
* `double[]` or an `Object[]` of boxed values depends on which ClassTag arrived, which bytecode does not record. So one
* check reads the library and the other runs it, they cover one question between them, and a change that weakens
* `double[]` or an `Object[]` of boxed values depends on which ClassTag arrived, which bytecode does not record. So
* one check reads the library and the other runs it, they cover one question between them, and a change that weakens
* either should be looking at the other in the same directory.
*
* It is why this module's *test* scope depends on `vecxt` while the analyzer in `src` does not: reading classfiles
Expand All @@ -121,8 +121,8 @@ object `package` extends ScalaModule:
/** The audit's scope, handed over as system properties.
*
* Explicit roots rather than walking `java.class.path` the way Phase 0 did. A classpath walk cannot distinguish
* "this module is clean" from "this module was never on the classpath", so coverage could shrink silently; with the
* roots written down here, `CoverageSuite` can assert the set is the intended one.
* "this module is clean" from "this module was never on the classpath", so coverage could shrink silently; with
* the roots written down here, `CoverageSuite` can assert the set is the intended one.
*/
override def forkArgs = Task {
val roots = Seq(
Expand Down
2 changes: 1 addition & 1 deletion experiments/src/cheatsheet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ object CheatsheetTest:
private def matrixReverseSlicing(): Unit =
val m = Matrix(Array(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0), 3, 3)
println(s"submatrix: ${m((m.rows - 1 until 0 by -1), ::).printMat}")
println(s"submatrix: ${m((m.rows - 1 until 0 by -1), ::).layout}")
println(s"submatrix: ${m((m.rows - 1 until 0 by -1), ::).layoutString}")
end matrixReverseSlicing

private def elementWiseOps(): Unit =
Expand Down
10 changes: 5 additions & 5 deletions experiments/src/mnist.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ import scala.annotation.targetName
s"pixelData shape: ${pixelData.shape}, pixelData rows: ${pixelData.rows}, pixelData cols: ${pixelData.cols}, pixelData rowStride: ${pixelData.rowStride}, pixelData colStride: ${pixelData.colStride} pixelData offset: ${pixelData.offset}"
)

println(s"x layout ${pixelData.layout}")
println(s"x layout ${pixelData.layoutString}")

println(s"weight1 shape: ${weight1.shape}, weight1 rows: ${weight1.rows}, weight1 cols: ${weight1.cols}")
println(s"weight2 shape: ${weight2.shape}, weight2 rows: ${weight2.rows}, weight2 cols: ${weight2.cols}")
Expand Down Expand Up @@ -160,8 +160,8 @@ def foward_prop(w1: Matrix[Double], b1: Array[Double], w2: Matrix[Double], b2: A
// println(s"weight1 shape: ${w1.shape}, weight1 rows: ${w1.rows}, weight1 cols: ${w1.cols}")
// println(s"weight2 shape: ${w2.shape}, weight2 rows: ${w2.rows}, weight2 cols: ${w2.cols}")

// println(s"m: ${x.layout}")
// println(s"b: ${w1.layout}")
// println(s"m: ${x.layoutString}")
// println(s"b: ${w1.layoutString}")
val z1 = (x @@ w1)
// z1 += b1
z1.mapRowsInPlace(r => r.tap(_ += b1))
Expand Down Expand Up @@ -239,8 +239,8 @@ def back_prop(
val m_inv = 1.0 / m
// println(s"m: $m, m_inv: $m_inv")
// println(s"Y shape: ${Y.shape}, Y rows: ${Y.rows}, Y cols: ${Y.cols} y colStride: ${Y.colStride}, y rowStride: ${Y.rowStride}, y offset: ${Y.offset}")
// println(a2.layout)
// println(Y.layout)
// println(a2.layoutString)
// println(Y.layoutString)

val dz2 = a2 - Y
val dw2 = m_inv * (a1.transpose @@ dz2)
Expand Down
12 changes: 6 additions & 6 deletions vecxt/src-js/doublematrix.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object JsDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) >= d
j += 1
end while
Expand All @@ -34,7 +34,7 @@ object JsDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) > d
j += 1
end while
Expand All @@ -52,7 +52,7 @@ object JsDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) <= d
j += 1
end while
Expand All @@ -70,7 +70,7 @@ object JsDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) < d
j += 1
end while
Expand All @@ -94,8 +94,8 @@ object JsDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val mIdx = m.offset + i * m.rowStride + j * m.colStride
val bIdx = bmat.offset + i * bmat.rowStride + j * bmat.colStride
val mIdx = m.layout.linearIndex(i, j)
val bIdx = bmat.layout.linearIndex(i, j)
newArr(i + j * m.rows) = if bmat.raw(bIdx) then m.raw(mIdx) else 0.0
j += 1
end while
Expand Down
12 changes: 6 additions & 6 deletions vecxt/src-jvm/doublematrix.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ object JvmDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val mIdx = m.offset + i * m.rowStride + j * m.colStride
val bIdx = bmat.offset + i * bmat.rowStride + j * bmat.colStride
val mIdx = m.layout.linearIndex(i, j)
val bIdx = bmat.layout.linearIndex(i, j)
newArr(i + j * m.rows) = if bmat.raw(bIdx) then m.raw(mIdx) else 0.0
j += 1
end while
Expand Down Expand Up @@ -154,7 +154,7 @@ object JvmDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) >= d
j += 1
end while
Expand All @@ -170,7 +170,7 @@ object JvmDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) > d
j += 1
end while
Expand All @@ -186,7 +186,7 @@ object JvmDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) <= d
j += 1
end while
Expand All @@ -202,7 +202,7 @@ object JvmDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) < d
j += 1
end while
Expand Down
14 changes: 7 additions & 7 deletions vecxt/src-jvm/floatmatrix.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ object JvmFloatMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val mIdx = m.offset + i * m.rowStride + j * m.colStride
val bIdx = bmat.offset + i * bmat.rowStride + j * bmat.colStride
val mIdx = m.layout.linearIndex(i, j)
val bIdx = bmat.layout.linearIndex(i, j)
newArr(i + j * m.rows) = if bmat.raw(bIdx) then m.raw(mIdx) else 0.0f
j += 1
end while
Expand Down Expand Up @@ -171,7 +171,7 @@ object JvmFloatMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) >= d
j += 1
end while
Expand All @@ -188,7 +188,7 @@ object JvmFloatMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) > d
j += 1
end while
Expand All @@ -205,7 +205,7 @@ object JvmFloatMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) <= d
j += 1
end while
Expand All @@ -222,7 +222,7 @@ object JvmFloatMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) < d
j += 1
end while
Expand Down Expand Up @@ -336,7 +336,7 @@ object JvmFloatMatrix:
sameDimMatCheck(m, mat1)
if sameDenseElementWiseMemoryLayoutCheck(m, mat1) then
val newArr = vecxt.floatarrays.-(m.raw)(mat1.raw)
Matrix[Float](newArr, m.rows, m.cols, m.rowStride, m.colStride, m.offset)
Matrix(newArr, m.layout)
else
val newArr = Array.ofDim[Float](m.numel)
val newMat =
Expand Down
4 changes: 2 additions & 2 deletions vecxt/src-jvm/intmatrix.scala
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ object JvmIntMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val mIdx = m.offset + i * m.rowStride + j * m.colStride
val bIdx = bmat.offset + i * bmat.rowStride + j * bmat.colStride
val mIdx = m.layout.linearIndex(i, j)
val bIdx = bmat.layout.linearIndex(i, j)
newArr(i + j * m.rows) = if bmat.raw(bIdx) then m.raw(mIdx) else 0
j += 1
end while
Expand Down
12 changes: 6 additions & 6 deletions vecxt/src-native/doublematrix_native.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ object NativeDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val mIdx = m.offset + i * m.rowStride + j * m.colStride
val bIdx = bmat.offset + i * bmat.rowStride + j * bmat.colStride
val mIdx = m.layout.linearIndex(i, j)
val bIdx = bmat.layout.linearIndex(i, j)
newArr(i + j * m.rows) = if bmat.raw(bIdx) then m.raw(mIdx) else 0.0
j += 1
end while
Expand Down Expand Up @@ -91,7 +91,7 @@ object NativeDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) >= d
j += 1
end while
Expand All @@ -107,7 +107,7 @@ object NativeDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) > d
j += 1
end while
Expand All @@ -125,7 +125,7 @@ object NativeDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) <= d
j += 1
end while
Expand All @@ -143,7 +143,7 @@ object NativeDoubleMatrix:
while i < m.rows do
var j = 0
while j < m.cols do
val srcIdx = m.offset + i * m.rowStride + j * m.colStride
val srcIdx = m.layout.linearIndex(i, j)
newArr(i + j * m.rows) = m.raw(srcIdx) < d
j += 1
end while
Expand Down
Loading
Loading