Skip to content

Commit 5d12fa4

Browse files
committed
GenASM: pipeline disk-write with building of classfiles
1 parent 0a9cea6 commit 5d12fa4

File tree

3 files changed

+91
-35
lines changed

3 files changed

+91
-35
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ trait BytecodeWriters {
3737
getFile(outputDirectory(sym), clsName, suffix)
3838

3939
trait BytecodeWriter {
40-
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol): Unit
40+
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile): Unit
4141
def close(): Unit = ()
4242
}
4343

@@ -48,7 +48,9 @@ trait BytecodeWriters {
4848
)
4949
val writer = new Jar(jfile).jarWriter(jarMainAttrs: _*)
5050

51-
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
51+
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile) {
52+
assert(outfile == null,
53+
"The outfile formal param is there just because ClassBytecodeWriter overrides this method and uses it.")
5254
val path = jclassName + ".class"
5355
val out = writer.newOutputStream(path)
5456

@@ -72,21 +74,21 @@ trait BytecodeWriters {
7274
try javap(Seq("-verbose", "dummy")) foreach (_.show())
7375
finally pw.close()
7476
}
75-
abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
76-
super.writeClass(label, jclassName, jclassBytes, sym)
77+
abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile) {
78+
super.writeClass(label, jclassName, jclassBytes, outfile)
7779

78-
val bytes = getFile(sym, jclassName, ".class").toByteArray
7980
val segments = jclassName.split("[./]")
8081
val javapFile = segments.foldLeft(baseDir: Path)(_ / _) changeExtension "javap" toFile;
8182

8283
javapFile.parent.createDirectory()
83-
emitJavap(bytes, javapFile)
84+
emitJavap(jclassBytes, javapFile)
8485
}
8586
}
8687

8788
trait ClassBytecodeWriter extends BytecodeWriter {
88-
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
89-
val outfile = getFile(sym, jclassName, ".class")
89+
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile) {
90+
assert(outfile != null,
91+
"Precisely this override requires its invoker to hand out a non-null AbstractFile.")
9092
val outstream = new DataOutputStream(outfile.bufferedOutput)
9193

9294
try outstream.write(jclassBytes, 0, jclassBytes.length)
@@ -98,8 +100,8 @@ trait BytecodeWriters {
98100
trait DumpBytecodeWriter extends BytecodeWriter {
99101
val baseDir = Directory(settings.Ydumpclasses.value).createDirectory()
100102

101-
abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
102-
super.writeClass(label, jclassName, jclassBytes, sym)
103+
abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile) {
104+
super.writeClass(label, jclassName, jclassBytes, outfile)
103105

104106
val pathName = jclassName
105107
var dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _) changeExtension "class" toFile;

src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
2929

3030
val phaseName = "jvm"
3131

32+
case class WorkUnit(label: String, jclassName: String, jclass: asm.ClassWriter, outF: AbstractFile)
33+
34+
type WorkUnitQueue = _root_.java.util.concurrent.LinkedBlockingQueue[WorkUnit]
35+
3236
/** Create a new phase */
3337
override def newPhase(p: Phase): Phase = new AsmPhase(p)
3438

@@ -148,6 +152,44 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
148152
}
149153
}
150154

155+
// -----------------------------------------------------------------------------------------
156+
// Allow overlapping disk write of classfiles with building of the next classfiles.
157+
// -----------------------------------------------------------------------------------------
158+
159+
val q = new WorkUnitQueue(500)
160+
161+
class WriteTask(bytecodeWriter: BytecodeWriter) extends _root_.java.lang.Runnable {
162+
163+
def run() {
164+
var stop = false
165+
try {
166+
while (!stop) {
167+
val WorkUnit(label, jclassName, jclass, outF) = q.take
168+
if(jclass eq null) { stop = true }
169+
else { writeIfNotTooBig(label, jclassName, jclass, outF) }
170+
}
171+
} catch {
172+
case ex: InterruptedException => throw ex
173+
}
174+
}
175+
176+
private def writeIfNotTooBig(label: String, jclassName: String, jclass: asm.ClassWriter, outF: AbstractFile) {
177+
try {
178+
val arr = jclass.toByteArray()
179+
bytecodeWriter.writeClass(label, jclassName, arr, outF)
180+
} catch {
181+
case e: java.lang.RuntimeException if(e.getMessage() == "Class file too large!") =>
182+
// TODO check where ASM throws the equivalent of CodeSizeTooBigException
183+
log("Skipped class "+jclassName+" because it exceeds JVM limits (it's too big or has methods that are too long).")
184+
}
185+
}
186+
187+
}
188+
189+
// -----------------------------------------------------------------------------------------
190+
// what AsmPhase does.
191+
// -----------------------------------------------------------------------------------------
192+
151193
override def run() {
152194

153195
if (settings.debug.value)
@@ -161,10 +203,14 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
161203
var sortedClasses = classes.values.toList sortBy ("" + _.symbol.fullName)
162204

163205
debuglog("Created new bytecode generator for " + classes.size + " classes.")
164-
val bytecodeWriter = initBytecodeWriter(sortedClasses filter isJavaEntryPoint)
165-
val plainCodeGen = new JPlainBuilder(bytecodeWriter)
166-
val mirrorCodeGen = new JMirrorBuilder(bytecodeWriter)
167-
val beanInfoCodeGen = new JBeanInfoBuilder(bytecodeWriter)
206+
val bytecodeWriter = initBytecodeWriter(sortedClasses filter isJavaEntryPoint)
207+
val needsOutfileForSymbol = bytecodeWriter.isInstanceOf[ClassBytecodeWriter]
208+
209+
val plainCodeGen = new JPlainBuilder(q, needsOutfileForSymbol)
210+
val mirrorCodeGen = new JMirrorBuilder(q, needsOutfileForSymbol)
211+
val beanInfoCodeGen = new JBeanInfoBuilder(q, needsOutfileForSymbol)
212+
213+
new _root_.java.lang.Thread(new WriteTask(bytecodeWriter)).start()
168214

169215
while(!sortedClasses.isEmpty) {
170216
val c = sortedClasses.head
@@ -187,6 +233,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
187233
classes -= c.symbol // GC opportunity
188234
}
189235

236+
q put WorkUnit(null, null, null, null)
237+
238+
while(!q.isEmpty) { _root_.java.lang.Thread.sleep(10) }
239+
190240
bytecodeWriter.close()
191241
classes.clear()
192242
reverseJavaName.clear()
@@ -448,7 +498,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
448498
val JAVA_LANG_STRING = asm.Type.getObjectType("java/lang/String")
449499

450500
/** basic functionality for class file building */
451-
abstract class JBuilder(bytecodeWriter: BytecodeWriter) {
501+
abstract class JBuilder(wuQ: WorkUnitQueue, needsOutfileForSymbol: Boolean) {
452502

453503
val EMPTY_JTYPE_ARRAY = Array.empty[asm.Type]
454504
val EMPTY_STRING_ARRAY = Array.empty[String]
@@ -504,17 +554,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
504554
// utitilies useful when emitting plain, mirror, and beaninfo classes.
505555
// -----------------------------------------------------------------------------------------
506556

507-
def writeIfNotTooBig(label: String, jclassName: String, jclass: asm.ClassWriter, sym: Symbol) {
508-
try {
509-
val arr = jclass.toByteArray()
510-
bytecodeWriter.writeClass(label, jclassName, arr, sym)
511-
} catch {
512-
case e: java.lang.RuntimeException if(e.getMessage() == "Class file too large!") =>
513-
// TODO check where ASM throws the equivalent of CodeSizeTooBigException
514-
log("Skipped class "+jclassName+" because it exceeds JVM limits (it's too big or has methods that are too long).")
515-
}
516-
}
517-
518557
/** Specialized array conversion to prevent calling
519558
* java.lang.reflect.Array.newInstance via TraversableOnce.toArray
520559
*/
@@ -728,11 +767,18 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
728767
}
729768
}
730769

770+
def enqueue(label: String, jclassName: String, jclass: asm.ClassWriter, sym: Symbol) {
771+
val outF: scala.tools.nsc.io.AbstractFile = {
772+
if(needsOutfileForSymbol) getFile(sym, jclassName, ".class") else null
773+
}
774+
wuQ put WorkUnit(label, jclassName, jclass, outF)
775+
}
776+
731777
} // end of class JBuilder
732778

733779

734780
/** functionality for building plain and mirror classes */
735-
abstract class JCommonBuilder(bytecodeWriter: BytecodeWriter) extends JBuilder(bytecodeWriter) {
781+
abstract class JCommonBuilder(wuQ: WorkUnitQueue, needsOutfileForSymbol: Boolean) extends JBuilder(wuQ, needsOutfileForSymbol) {
736782

737783
// -----------------------------------------------------------------------------------------
738784
// more constants
@@ -1290,8 +1336,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
12901336
case class BlockInteval(start: BasicBlock, end: BasicBlock)
12911337

12921338
/** builder of plain classes */
1293-
class JPlainBuilder(bytecodeWriter: BytecodeWriter)
1294-
extends JCommonBuilder(bytecodeWriter)
1339+
class JPlainBuilder(wuQ: WorkUnitQueue, needsOutfileForSymbol: Boolean)
1340+
extends JCommonBuilder(wuQ, needsOutfileForSymbol)
12951341
with JAndroidBuilder {
12961342

12971343
val MIN_SWITCH_DENSITY = 0.7
@@ -1445,7 +1491,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
14451491

14461492
addInnerClasses(clasz.symbol, jclass)
14471493
jclass.visitEnd()
1448-
writeIfNotTooBig("" + c.symbol.name, thisName, jclass, c.symbol)
1494+
enqueue("" + c.symbol.name, thisName, jclass, c.symbol)
14491495

14501496
}
14511497

@@ -2863,7 +2909,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
28632909

28642910

28652911
/** builder of mirror classes */
2866-
class JMirrorBuilder(bytecodeWriter: BytecodeWriter) extends JCommonBuilder(bytecodeWriter) {
2912+
class JMirrorBuilder(wuQ: WorkUnitQueue, needsOutfileForSymbol: Boolean) extends JCommonBuilder(wuQ, needsOutfileForSymbol) {
28672913

28682914
private var cunit: CompilationUnit = _
28692915
def getCurrentCUnit(): CompilationUnit = cunit;
@@ -2907,15 +2953,15 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
29072953

29082954
addInnerClasses(modsym, mirrorClass)
29092955
mirrorClass.visitEnd()
2910-
writeIfNotTooBig("" + modsym.name, mirrorName, mirrorClass, modsym)
2956+
enqueue("" + modsym.name, mirrorName, mirrorClass, modsym)
29112957
}
29122958

29132959

29142960
} // end of class JMirrorBuilder
29152961

29162962

29172963
/** builder of bean info classes */
2918-
class JBeanInfoBuilder(bytecodeWriter: BytecodeWriter) extends JBuilder(bytecodeWriter) {
2964+
class JBeanInfoBuilder(wuQ: WorkUnitQueue, needsOutfileForSymbol: Boolean) extends JBuilder(wuQ, needsOutfileForSymbol) {
29192965

29202966
/**
29212967
* Generate a bean info class that describes the given class.
@@ -3036,7 +3082,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
30363082
addInnerClasses(clasz.symbol, beanInfoClass)
30373083
beanInfoClass.visitEnd()
30383084

3039-
writeIfNotTooBig("BeanInfo ", beanInfoName, beanInfoClass, clasz.symbol)
3085+
enqueue("BeanInfo ", beanInfoName, beanInfoClass, clasz.symbol)
30403086
}
30413087

30423088
} // end of class JBeanInfoBuilder

src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
183183
class BytecodeGenerator(bytecodeWriter: BytecodeWriter) extends BytecodeUtil {
184184
def this() = this(new ClassBytecodeWriter { })
185185
def debugLevel = settings.debuginfo.indexOfChoice
186-
import bytecodeWriter.writeClass
187186

188187
val MIN_SWITCH_DENSITY = 0.7
189188
val INNER_CLASSES_FLAGS =
@@ -344,6 +343,15 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
344343
writeClass("" + sym.name, jclass.getName(), toByteArray(jclass), sym)
345344
}
346345

346+
val needsOutfileForSymbol = bytecodeWriter.isInstanceOf[ClassBytecodeWriter]
347+
348+
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
349+
val outF: scala.tools.nsc.io.AbstractFile = {
350+
if(needsOutfileForSymbol) getFile(sym, jclassName, ".class") else null
351+
}
352+
bytecodeWriter.writeClass(label, jclassName, jclassBytes, outF)
353+
}
354+
347355
/** Returns the ScalaSignature annotation if it must be added to this class,
348356
* none otherwise; furthermore, it adds to `jclass` the ScalaSig marker
349357
* attribute (marking that a scala signature annotation is present) or the

0 commit comments

Comments
 (0)