Skip to content

Instantly share code, notes, and snippets.

@rednaxelafx
Created June 26, 2012 07:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rednaxelafx/2994217 to your computer and use it in GitHub Desktop.
Save rednaxelafx/2994217 to your computer and use it in GitHub Desktop.
OptimizeStringConcat in HS24 will optimize the result of one StringBuilder to be used multiple times in another StringBuilder
$ java -version
java version "1.8.0-ea"
Java(TM) SE Runtime Environment (build 1.8.0-ea-b43)
OpenJDK 64-Bit Server VM (build 24.0-b14-internal-fastdebug, mixed mode)
$ java -XX:-TieredCompilation -XX:+PrintCompilation -XX:+PrintOptimizeStringConcat -XX:CompileCommand='exclude,MultipleUse,main' MultipleUse
CompilerOracle: exclude MultipleUse.main
268 1 java.lang.String::equals (81 bytes)
303 2 n java.lang.System::arraycopy (0 bytes) (static)
303 3 java.lang.String::length (6 bytes)
309 4 java.lang.Object::<init> (1 bytes)
312 5 java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes)
313 6 java.lang.AbstractStringBuilder::append (48 bytes)
314 7 java.lang.String::getChars (62 bytes)
318 8 java.lang.StringBuilder::append (8 bytes)
330 9 java.lang.Math::min (11 bytes)
332 10 java.lang.String::<init> (67 bytes)
332 11 java.util.Arrays::copyOfRange (63 bytes)
338 12 java.lang.AbstractStringBuilder::<init> (12 bytes)
339 13 java.lang.StringBuilder::toString (17 bytes)
341 14 java.lang.StringBuilder::<init> (18 bytes)
349 15 MultipleUse::foo (37 bytes)
considering toString call in MultipleUse::foo @ bci:31
fusion would succeed (1 0) for MultipleUse::foo @ bci:19
105 Proj === 85 [[ 161 144 119 144 213 213 ]] #5 Oop:java/lang/String:exact * !jvms: MultipleUse::foo @ bci:15
105 Proj === 85 [[ 161 144 119 144 213 213 ]] #5 Oop:java/lang/String:exact * !jvms: MultipleUse::foo @ bci:15
0--> 161 CallStaticJava === 151 156 157 8 1 ( 136 105 1 ) [[ 162 163 164 166 175 174 ]] # Static java.lang.StringBuilder::append java/lang/StringBuilder:exact * ( java/lang/StringBuilder:NotNull:exact *, java/lang/String:exact * ) MultipleUse::foo @ bci:28 !jvms: MultipleUse::foo @ bci:28
1--> 179 CallStaticJava === 185 174 175 8 1 ( 192 1 ) [[ 193 194 195 197 206 205 ]] # Static java.lang.StringBuilder::toString java/lang/String:exact * ( java/lang/StringBuilder:NotNull:exact * ) MultipleUse::foo @ bci:31 !jvms: MultipleUse::foo @ bci:31
2--> 144 CallStaticJava === 133 130 118 8 1 ( 136 105 105 136 ) [[ 145 146 147 157 156 ]] # Static java.lang.StringBuilder::<init> void ( java/lang/StringBuilder:NotNull:exact *, java/lang/String:exact * ) MultipleUse::foo @ bci:24 !jvms: MultipleUse::foo @ bci:24
3--> 119 Allocate === 108 113 114 8 1 ( 24 22 23 1 105 ) [[ 120 121 122 129 130 131 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, int ) MultipleUse::foo @ bci:19 !jvms: MultipleUse::foo @ bci:19
4--> 185 IfTrue === 184 [[ 179 192 ]] #1 !jvms: MultipleUse::foo @ bci:31
considering toString call in MultipleUse::foo @ bci:15
fusion would succeed (1 0) for MultipleUse::foo @ bci:3
21 ConP === 0 [[ 63 46 26 46 214 214 ]] #java/lang/String:exact * Oop:java/lang/String:exact *
21 ConP === 0 [[ 63 46 26 46 214 214 ]] #java/lang/String:exact * Oop:java/lang/String:exact *
0--> 63 CallStaticJava === 53 58 59 8 1 ( 43 21 1 ) [[ 64 65 66 68 77 76 ]] # Static java.lang.StringBuilder::append java/lang/StringBuilder:exact * ( java/lang/StringBuilder:NotNull:exact *, java/lang/String:exact * ) MultipleUse::foo @ bci:12 !jvms: MultipleUse::foo @ bci:12
1--> 85 CallStaticJava === 92 76 77 8 1 ( 100 1 ) [[ 101 102 103 105 114 113 ]] # Static java.lang.StringBuilder::toString java/lang/String:exact * ( java/lang/StringBuilder:NotNull:exact * ) MultipleUse::foo @ bci:15 !jvms: MultipleUse::foo @ bci:15
2--> 46 CallStaticJava === 40 37 25 8 1 ( 43 21 21 43 ) [[ 47 48 49 59 58 ]] # Static java.lang.StringBuilder::<init> void ( java/lang/StringBuilder:NotNull:exact *, java/lang/String:exact * ) MultipleUse::foo @ bci:8 !jvms: MultipleUse::foo @ bci:8
3--> 26 Allocate === 5 6 7 8 1 ( 24 22 23 1 21 ) [[ 27 28 29 36 37 38 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, int ) MultipleUse::foo @ bci:3 !jvms: MultipleUse::foo @ bci:3
4--> 92 IfTrue === 91 [[ 85 100 ]] #1 !jvms: MultipleUse::foo @ bci:15
considering stacked concats
### Excluding compile: static MultipleUse::main
fusion would succeed (2 0) for MultipleUse::foo @ bci:3
21 ConP === 0 [[ 63 46 26 46 214 214 215 215 215 215 ]] #java/lang/String:exact * Oop:java/lang/String:exact *
21 ConP === 0 [[ 63 46 26 46 214 214 215 215 215 215 ]] #java/lang/String:exact * Oop:java/lang/String:exact *
21 ConP === 0 [[ 63 46 26 46 214 214 215 215 215 215 ]] #java/lang/String:exact * Oop:java/lang/String:exact *
21 ConP === 0 [[ 63 46 26 46 214 214 215 215 215 215 ]] #java/lang/String:exact * Oop:java/lang/String:exact *
0--> 161 CallStaticJava === 151 156 157 8 1 ( 136 105 1 ) [[ 162 163 164 166 175 174 ]] # Static java.lang.StringBuilder::append java/lang/StringBuilder:exact * ( java/lang/StringBuilder:NotNull:exact *, java/lang/String:exact * ) MultipleUse::foo @ bci:28 !jvms: MultipleUse::foo @ bci:28
1--> 179 CallStaticJava === 185 174 175 8 1 ( 192 1 ) [[ 193 194 195 197 206 205 ]] # Static java.lang.StringBuilder::toString java/lang/String:exact * ( java/lang/StringBuilder:NotNull:exact * ) MultipleUse::foo @ bci:31 !jvms: MultipleUse::foo @ bci:31
2--> 144 CallStaticJava === 133 130 118 8 1 ( 136 105 105 136 ) [[ 145 146 147 157 156 ]] # Static java.lang.StringBuilder::<init> void ( java/lang/StringBuilder:NotNull:exact *, java/lang/String:exact * ) MultipleUse::foo @ bci:24 !jvms: MultipleUse::foo @ bci:24
3--> 119 Allocate === 108 113 114 8 1 ( 24 22 23 1 105 ) [[ 120 121 122 129 130 131 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, int ) MultipleUse::foo @ bci:19 !jvms: MultipleUse::foo @ bci:19
4--> 63 CallStaticJava === 53 58 59 8 1 ( 43 21 1 ) [[ 64 65 66 68 77 76 ]] # Static java.lang.StringBuilder::append java/lang/StringBuilder:exact * ( java/lang/StringBuilder:NotNull:exact *, java/lang/String:exact * ) MultipleUse::foo @ bci:12 !jvms: MultipleUse::foo @ bci:12
5--> 85 CallStaticJava === 92 76 77 8 1 ( 100 1 ) [[ 101 102 103 105 114 113 ]] # Static java.lang.StringBuilder::toString java/lang/String:exact * ( java/lang/StringBuilder:NotNull:exact * ) MultipleUse::foo @ bci:15 !jvms: MultipleUse::foo @ bci:15
6--> 46 CallStaticJava === 40 37 25 8 1 ( 43 21 21 43 ) [[ 47 48 49 59 58 ]] # Static java.lang.StringBuilder::<init> void ( java/lang/StringBuilder:NotNull:exact *, java/lang/String:exact * ) MultipleUse::foo @ bci:8 !jvms: MultipleUse::foo @ bci:8
7--> 26 Allocate === 5 6 7 8 1 ( 24 22 23 1 21 ) [[ 27 28 29 36 37 38 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, int ) MultipleUse::foo @ bci:3 !jvms: MultipleUse::foo @ bci:3
8--> 185 IfTrue === 184 [[ 179 192 ]] #1 !jvms: MultipleUse::foo @ bci:31
9--> 92 IfTrue === 91 [[ 85 100 ]] #1 !jvms: MultipleUse::foo @ bci:15
stacking would succeed
done
$
Decoding compiled method 0x00007f9877cbbf90:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} 'foo' '()Ljava/lang/String;' in 'MultipleUse'
# [sp+0x30] (sp of caller)
;; N1: # B1 <- B12 B5 Freq: 1
;; B1: # B6 B2 <- BLOCK HEAD IS JUNK Freq: 1
0x00007f9877cbc100: mov %eax,-0x16000(%rsp)
0x00007f9877cbc107: push %rbp
0x00007f9877cbc108: sub $0x20,%rsp ;*new ; - MultipleUse::foo@19 (line 5)
0x00007f9877cbc10c: mov 0x70(%r15),%rbp
0x00007f9877cbc110: mov %rbp,%r10
0x00007f9877cbc113: add $0x48,%r10
0x00007f9877cbc117: cmp 0x80(%r15),%r10
0x00007f9877cbc11e: jae 0x00007f9877cbc353
;; B2: # B3 <- B1 Freq: 0.9999
0x00007f9877cbc124: mov %r10,0x70(%r15)
0x00007f9877cbc128: prefetchnta 0xc0(%r10)
0x00007f9877cbc130: movq $0x1,0x0(%rbp)
0x00007f9877cbc138: prefetchnta 0x100(%r10)
0x00007f9877cbc140: movl $0xeffc00ba,0x8(%rbp) ; {oop({type array char})}
0x00007f9877cbc147: prefetchnta 0x140(%r10)
0x00007f9877cbc14f: movl $0x1c,0xc(%rbp)
0x00007f9877cbc156: prefetchnta 0x180(%r10)
;; B3: # B8 B4 <- B7 B2 Freq: 1
0x00007f9877cbc15e: mov %rbp,%rbx
0x00007f9877cbc161: add $0x3a,%rbx
0x00007f9877cbc165: mov %rbp,%r10
0x00007f9877cbc168: add $0x2c,%r10
0x00007f9877cbc16c: mov %r10,(%rsp)
0x00007f9877cbc170: mov %rbp,%r13
0x00007f9877cbc173: add $0x1e,%r13
0x00007f9877cbc177: mov %rbp,%rsi
0x00007f9877cbc17a: add $0x10,%rsi
0x00007f9877cbc17e: mov $0x7d704ef38,%r10 ; {oop([C)}
0x00007f9877cbc188: mov $0x7d704ef38,%r14 ; {oop([C)}
0x00007f9877cbc192: add $0x10,%r14
0x00007f9877cbc196: mov %r14,%rdi
0x00007f9877cbc199: mov $0x7,%edx
0x00007f9877cbc19e: mov $0x7f9877c99aa0,%r10
0x00007f9877cbc1a8: callq *%r10
0x00007f9877cbc1ab: mov %r14,%rdi
0x00007f9877cbc1ae: mov %r13,%rsi
0x00007f9877cbc1b1: mov $0x7,%edx
0x00007f9877cbc1b6: mov $0x7f9877c99aa0,%r10
0x00007f9877cbc1c0: callq *%r10
0x00007f9877cbc1c3: mov %r14,%rdi
0x00007f9877cbc1c6: mov (%rsp),%rsi
0x00007f9877cbc1ca: mov $0x7,%edx
0x00007f9877cbc1cf: mov $0x7f9877c99aa0,%r10
0x00007f9877cbc1d9: callq *%r10
0x00007f9877cbc1dc: mov %r14,%rdi
0x00007f9877cbc1df: mov %rbx,%rsi
0x00007f9877cbc1e2: mov $0x7,%edx
0x00007f9877cbc1e7: mov $0x7f9877c99aa0,%r10
0x00007f9877cbc1f1: callq *%r10
0x00007f9877cbc1f4: mov 0x70(%r15),%rax
0x00007f9877cbc1f8: mov %rax,%r10
0x00007f9877cbc1fb: add $0x18,%r10
0x00007f9877cbc1ff: cmp 0x80(%r15),%r10
0x00007f9877cbc206: jae 0x00007f9877cbc370
;; B4: # B5 <- B3 Freq: 0.9999
0x00007f9877cbc20c: mov %r10,0x70(%r15)
0x00007f9877cbc210: prefetchnta 0xc0(%r10)
0x00007f9877cbc218: mov $0xeffc15e7,%r11d ; {oop('java/lang/String')}
0x00007f9877cbc21e: mov 0xb0(%r12,%r11,8),%r10
0x00007f9877cbc226: mov %r10,(%rax)
0x00007f9877cbc229: movl $0xeffc15e7,0x8(%rax) ; {oop('java/lang/String')}
0x00007f9877cbc230: mov %r12,0x10(%rax)
;; B5: # N1 <- B9 B4 Freq: 1
0x00007f9877cbc234: push %r10
0x00007f9877cbc236: cmp 0x5481ed3(%rip),%r12 # 0x00007f987d13e110
; {external_word}
0x00007f9877cbc23d: je 0x00007f9877cbc2ba
0x00007f9877cbc243: mov %rsp,-0x28(%rsp)
0x00007f9877cbc248: sub $0x80,%rsp
0x00007f9877cbc24f: mov %rax,0x78(%rsp)
0x00007f9877cbc254: mov %rcx,0x70(%rsp)
0x00007f9877cbc259: mov %rdx,0x68(%rsp)
0x00007f9877cbc25e: mov %rbx,0x60(%rsp)
0x00007f9877cbc263: mov %rbp,0x50(%rsp)
0x00007f9877cbc268: mov %rsi,0x48(%rsp)
0x00007f9877cbc26d: mov %rdi,0x40(%rsp)
0x00007f9877cbc272: mov %r8,0x38(%rsp)
0x00007f9877cbc277: mov %r9,0x30(%rsp)
0x00007f9877cbc27c: mov %r10,0x28(%rsp)
0x00007f9877cbc281: mov %r11,0x20(%rsp)
0x00007f9877cbc286: mov %r12,0x18(%rsp)
0x00007f9877cbc28b: mov %r13,0x10(%rsp)
0x00007f9877cbc290: mov %r14,0x8(%rsp)
0x00007f9877cbc295: mov %r15,(%rsp)
0x00007f9877cbc299: mov $0x7f987cc083a8,%rdi ; {external_word}
0x00007f9877cbc2a3: mov $0x7f9877cbc243,%rsi ; {internal_word}
0x00007f9877cbc2ad: mov %rsp,%rdx
0x00007f9877cbc2b0: and $0xfffffffffffffff0,%rsp
0x00007f9877cbc2b4: callq 0x00007f987c20b740 ; {runtime_call}
0x00007f9877cbc2b9: hlt
0x00007f9877cbc2ba: pop %r10
0x00007f9877cbc2bc: test %rbp,%rbp
0x00007f9877cbc2bf: jne 0x00007f9877cbc33c
0x00007f9877cbc2c5: mov %rsp,-0x28(%rsp)
0x00007f9877cbc2ca: sub $0x80,%rsp
0x00007f9877cbc2d1: mov %rax,0x78(%rsp)
0x00007f9877cbc2d6: mov %rcx,0x70(%rsp)
0x00007f9877cbc2db: mov %rdx,0x68(%rsp)
0x00007f9877cbc2e0: mov %rbx,0x60(%rsp)
0x00007f9877cbc2e5: mov %rbp,0x50(%rsp)
0x00007f9877cbc2ea: mov %rsi,0x48(%rsp)
0x00007f9877cbc2ef: mov %rdi,0x40(%rsp)
0x00007f9877cbc2f4: mov %r8,0x38(%rsp)
0x00007f9877cbc2f9: mov %r9,0x30(%rsp)
0x00007f9877cbc2fe: mov %r10,0x28(%rsp)
0x00007f9877cbc303: mov %r11,0x20(%rsp)
0x00007f9877cbc308: mov %r12,0x18(%rsp)
0x00007f9877cbc30d: mov %r13,0x10(%rsp)
0x00007f9877cbc312: mov %r14,0x8(%rsp)
0x00007f9877cbc317: mov %r15,(%rsp)
0x00007f9877cbc31b: mov $0x7f987cc083e8,%rdi ; {external_word}
0x00007f9877cbc325: mov $0x7f9877cbc2c5,%rsi ; {internal_word}
0x00007f9877cbc32f: mov %rsp,%rdx
0x00007f9877cbc332: and $0xfffffffffffffff0,%rsp
0x00007f9877cbc336: callq 0x00007f987c20b740 ; {runtime_call}
0x00007f9877cbc33b: hlt
0x00007f9877cbc33c: mov %rbp,%r10
0x00007f9877cbc33f: shr $0x3,%r10
0x00007f9877cbc343: mov %r10d,0xc(%rax)
0x00007f9877cbc347: add $0x20,%rsp
0x00007f9877cbc34b: pop %rbp
0x00007f9877cbc34c: test %eax,0x60ffcae(%rip) # 0x00007f987ddbc000
; {poll_return}
0x00007f9877cbc352: retq
;; B6: # B11 B7 <- B1 Freq: 0.000100017
0x00007f9877cbc353: mov $0x77fe005d0,%rsi ; {oop({type array char})}
0x00007f9877cbc35d: mov $0x1c,%edx
0x00007f9877cbc362: nop
0x00007f9877cbc363: callq 0x00007f9877cb5160 ; OopMap{off=616}
;*new ; - MultipleUse::foo@3 (line 4)
; {runtime_call}
;; B7: # B3 <- B6 Freq: 0.000100015
0x00007f9877cbc368: mov %rax,%rbp
0x00007f9877cbc36b: jmpq 0x00007f9877cbc15e
;; B8: # B10 B9 <- B3 Freq: 0.000100017
0x00007f9877cbc370: mov $0x77fe0af38,%rsi ; {oop('java/lang/String')}
0x00007f9877cbc37a: nop
0x00007f9877cbc37b: callq 0x00007f9877cb5220 ; OopMap{rbp=Oop off=640}
;*new ; - MultipleUse::foo@3 (line 4)
; {runtime_call}
;; B9: # B5 <- B8 Freq: 0.000100015
0x00007f9877cbc380: jmpq 0x00007f9877cbc234
;; B10: # B12 <- B8 Freq: 1.00017e-09
0x00007f9877cbc385: mov %rax,%rsi
0x00007f9877cbc388: jmp 0x00007f9877cbc38d
;; B11: # B12 <- B6 Freq: 1.00017e-09
0x00007f9877cbc38a: mov %rax,%rsi ;*invokespecial <init>
; - MultipleUse::foo@8 (line 4)
;; B12: # N1 <- B11 B10 Freq: 2.00033e-09
0x00007f9877cbc38d: add $0x20,%rsp
0x00007f9877cbc391: pop %rbp
0x00007f9877cbc392: jmpq 0x00007f9877cb7f60 ; {runtime_call}
0x00007f9877cbc397: hlt
0x00007f9877cbc398: hlt
0x00007f9877cbc399: hlt
0x00007f9877cbc39a: hlt
0x00007f9877cbc39b: hlt
0x00007f9877cbc39c: hlt
0x00007f9877cbc39d: hlt
0x00007f9877cbc39e: hlt
0x00007f9877cbc39f: hlt
[Exception Handler]
[Stub Code]
0x00007f9877cbc3a0: jmpq 0x00007f9877c9ea60 ; {no_reloc}
[Deopt Handler Code]
0x00007f9877cbc3a5: callq 0x00007f9877cbc3aa
0x00007f9877cbc3aa: subq $0x5,(%rsp)
0x00007f9877cbc3af: jmpq 0x00007f9877c906a0 ; {runtime_call}
0x00007f9877cbc3b4: hlt
0x00007f9877cbc3b5: hlt
0x00007f9877cbc3b6: hlt
0x00007f9877cbc3b7: hlt
================================================
{method}
- klass: {other class}
- this oop: 0x000000078004c750
- method holder: 'MultipleUse'
- constants: 0x000000078004c2a0 constant pool [80] for 'MultipleUse' cache=0x000000078004cb48
- access: 0x81000009 public static
- name: 'foo'
- signature: '()Ljava/lang/String;'
- max stack: 3
- max locals: 1
- size of params: 0
- method size: 16
- vtable index: -2
- i2i entry: 0x00007ff7a965d2a0
- adapter: 0x0000000000f01378
- compiled entry 0x00007ff7a970d3ad
- code size: 37
- code start: 0x000000078004c718
- code end (excl): 0x000000078004c73d
- method data: 0x000000078004e4d0
- checked ex length: 0
- linenumber start: 0x000000078004c73d
- localvar length: 1
- localvar start: 0x000000078004c742
#
# java/lang/String:exact * ( )
#
# -- Old rsp -- Framesize: 48 --
#r191 rsp+44: in_preserve
#r190 rsp+40: return address
#r189 rsp+36: in_preserve
#r188 rsp+32: saved fp register
#r187 rsp+28: pad2, stack alignment
#r186 rsp+24: pad2, stack alignment
#r185 rsp+20: Fixed slot 1
#r184 rsp+16: Fixed slot 0
#r195 rsp+12: spill
#r194 rsp+ 8: spill
#r193 rsp+ 4: spill
#r192 rsp+ 0: spill
#
abababab N1: # B1 <- B12 B5 Freq: 1
abababab
000 B1: # B6 B2 <- BLOCK HEAD IS JUNK Freq: 1
000 # stack bang
pushq rbp # Save rbp
subq rsp, #32 # Create frame
00c
00c # TLS is in R15
00c movq RBP, [R15 + #112 (8-bit)] # ptr
010 movq R10, RBP # spill
013 addq R10, #72 # ptr
017 # TLS is in R15
017 cmpq R10, [R15 + #128 (32-bit)] # raw ptr
01e jnb,u B6 P=0.000100 C=-1.000000
01e
024 B2: # B3 <- B1 Freq: 0.9999
024 # TLS is in R15
024 movq [R15 + #112 (8-bit)], R10 # ptr
028 PREFETCHNTA [R10 + #192 (32-bit)] # Prefetch allocation to non-temporal cache for write
030 movq [RBP], 0x0000000000000001 # ptr
038 PREFETCHNTA [R10 + #256 (32-bit)] # Prefetch allocation to non-temporal cache for write
040 movl [RBP + #8 (8-bit)], narrowoop: precise klass [C: 0x00007ff7a000f8b8:Constant:exact * # compressed ptr
047 PREFETCHNTA [R10 + #320 (32-bit)] # Prefetch allocation to non-temporal cache for write
04f movl [RBP + #12 (8-bit)], #28 # int
056 PREFETCHNTA [R10 + #384 (32-bit)] # Prefetch allocation to non-temporal cache for write
056
05e B3: # B8 B4 <- B7 B2 Freq: 1
05e
05e MEMBAR-storestore (empty encoding)
05e # checkcastPP of RBP
05e movq RBX, RBP # spill
061 addq RBX, #58 # ptr
065 movq R10, RBP # spill
068 addq R10, #44 # ptr
06c movq [rsp + #0], R10 # spill
070 movq R13, RBP # spill
073 addq R13, #30 # ptr
077 movq RSI, RBP # spill
07a addq RSI, #16 # ptr
07e movq R10, char[int:7]<ciTypeArray length=7 type=<ciTypeArrayKlass name=[C ident=712 PERM address=0xa000f8b8> ident=734 SCAVENGABLE address=0x100f5a8> * # ptr
088 movq R14, char[int:7]<ciTypeArray length=7 type=<ciTypeArrayKlass name=[C ident=712 PERM address=0xa000f8b8> ident=734 SCAVENGABLE address=0x100f5a8> * # ptr
092 addq R14, #16 # ptr
096 movq RDI, R14 # spill
099 movl RDX, #7 # long (unsigned 32-bit)
09e call_leaf_nofp,runtime jshort_disjoint_arraycopy
No JVM State Info
#
0ab movq RDI, R14 # spill
0ae movq RSI, R13 # spill
0b1 movl RDX, #7 # long (unsigned 32-bit)
0b6 call_leaf_nofp,runtime jshort_disjoint_arraycopy
No JVM State Info
#
0c3 movq RDI, R14 # spill
0c6 movq RSI, [rsp + #0] # spill
0ca movl RDX, #7 # long (unsigned 32-bit)
0cf call_leaf_nofp,runtime jshort_disjoint_arraycopy
No JVM State Info
#
0dc movq RDI, R14 # spill
0df movq RSI, RBX # spill
0e2 movl RDX, #7 # long (unsigned 32-bit)
0e7 call_leaf_nofp,runtime jshort_disjoint_arraycopy
No JVM State Info
#
0f4 # TLS is in R15
0f4 movq RAX, [R15 + #112 (8-bit)] # ptr
0f8 movq R10, RAX # spill
0fb addq R10, #24 # ptr
0ff # TLS is in R15
0ff cmpq R10, [R15 + #128 (32-bit)] # raw ptr
106 jnb,u B8 P=0.000100 C=-1.000000
106
10c B4: # B5 <- B3 Freq: 0.9999
10c # TLS is in R15
10c movq [R15 + #112 (8-bit)], R10 # ptr
110 PREFETCHNTA [R10 + #192 (32-bit)] # Prefetch allocation to non-temporal cache for write
118 movl R11, narrowoop: precise klass java/lang/String: 0x0000000000f78498:Constant:exact * # compressed ptr
11e movq R10, [R12 + R11 << 3 + #176] (compressed oop addressing) # ptr
126 movq [RAX], R10 # ptr
129 movl [RAX + #8 (8-bit)], narrowoop: precise klass java/lang/String: 0x0000000000f78498:Constant:exact * # compressed ptr
130 movq [RAX + #16 (8-bit)], R12 # long (R12_heapbase==0)
130
134 B5: # N1 <- B9 B4 Freq: 1
134 encode_heap_oop_not_null R10,RBP
243 movl [RAX + #12 (8-bit)], R10 # compressed ptr
247
247 MEMBAR-storestore (empty encoding)
247 # checkcastPP of RAX
247 addq rsp, 32 # Destroy frame
popq rbp
testl rax, [rip + #offset_to_poll_page] # Safepoint: poll for GC
252 ret
252
253 B6: # B11 B7 <- B1 Freq: 0.000100017
253 movq RSI, precise klass [C: 0x00007ff7a000f8b8:Constant:exact * # ptr
25d movl RDX, #28 # int
nop # 1 bytes pad for loops and calls
263 call,static wrapper for: _new_array_Java
# MultipleUse::foo @ bci:3 L[0]=#Ptr0x000000000100ba68
# OopMap{off=616}
268
268 B7: # B3 <- B6 Freq: 0.000100015
# Block is sole successor of call
268 movq RBP, RAX # spill
26b jmp B3
26b
270 B8: # B10 B9 <- B3 Freq: 0.000100017
270 movq RSI, precise klass java/lang/String: 0x0000000000f78498:Constant:exact * # ptr
nop # 1 bytes pad for loops and calls
27b call,static wrapper for: _new_instance_Java
# MultipleUse::foo @ bci:3 L[0]=#Ptr0x000000000100ba68
# OopMap{rbp=Oop off=640}
280
280 B9: # B5 <- B8 Freq: 0.000100015
# Block is sole successor of call
280 jmp B5
280
285 B10: # B12 <- B8 Freq: 1.00017e-09
285 # exception oop is in rax; no code emitted
285 movq RSI, RAX # spill
288 jmp,s B12
288
28a B11: # B12 <- B6 Freq: 1.00017e-09
28a # exception oop is in rax; no code emitted
28a movq RSI, RAX # spill
28a
28d B12: # N1 <- B11 B10 Freq: 2.00033e-09
28d addq rsp, 32 # Destroy frame
popq rbp
292 jmp rethrow_stub
292
// pseudo-code of the actual generated code
public static String foo() {
// String s = "testing";
// s_value is a oop constant directly embedded in generated code.
// The String instance oop is no longer referenced
final char[] s_value = "testing".value;
final int len = 7 + 7 + 7 + 7; // constant folded to 28
char[] value = allocate_array(char[].klass, len); // skip zeroing contents
final char* srcptr = &s_value[0];
// s = new StringBuilder(s).append(s).toString();
// s = new StringBuilder(s).append(s).toString();
// jshort_disjoint_arraycopy(srcptr, dstptr, length);
jshort_disjoint_arraycopy(srcptr, &value[0], 7);
jshort_disjoint_arraycopy(srcptr, &value[7], 7);
jshort_disjoint_arraycopy(srcptr, &value[14], 7);
jshort_disjoint_arraycopy(srcptr, &value[21], 7);
// This sequence is generated for the case where
// String.count and String.offset have been removed. (CR 6924259)
String result = allocate_instance(String.klass);
result.value = value;
return result;
}
public class MultipleUse {
public static String foo() {
String s = "testing";
s = new StringBuilder(s).append(s).toString();
s = new StringBuilder(s).append(s).toString();
return s;
}
public static void main(String[] args) throws Exception {
final String s = "testing";
for (int i = 0; i < 20000; i++) {
if (!(s + s + s + s).equals(foo())) {
System.out.println("bad string concat");
}
}
System.out.println("done");
System.in.read();
}
}
$ javap -verbose -private MultipleUse
Compiled from "MultipleUse.java"
public class MultipleUse extends java.lang.Object
SourceFile: "MultipleUse.java"
minor version: 0
major version: 50
Constant pool:
const #1 = Method #17.#41; // java/lang/Object."<init>":()V
const #2 = String #42; // testing
const #3 = class #43; // java/lang/StringBuilder
const #4 = Method #3.#44; // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
const #5 = Method #3.#45; // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
const #6 = Method #3.#46; // java/lang/StringBuilder.toString:()Ljava/lang/String;
const #7 = String #47; // testingtestingtestingtesting
const #8 = Method #16.#48; // MultipleUse.foo:()Ljava/lang/String;
const #9 = Method #49.#50; // java/lang/String.equals:(Ljava/lang/Object;)Z
const #10 = Field #51.#52; // java/lang/System.out:Ljava/io/PrintStream;
const #11 = String #53; // bad string concat
const #12 = Method #54.#55; // java/io/PrintStream.println:(Ljava/lang/String;)V
const #13 = String #56; // done
const #14 = Field #51.#57; // java/lang/System.in:Ljava/io/InputStream;
const #15 = Method #58.#59; // java/io/InputStream.read:()I
const #16 = class #60; // MultipleUse
const #17 = class #61; // java/lang/Object
const #18 = Asciz <init>;
const #19 = Asciz ()V;
const #20 = Asciz Code;
const #21 = Asciz LineNumberTable;
const #22 = Asciz LocalVariableTable;
const #23 = Asciz this;
const #24 = Asciz LMultipleUse;;
const #25 = Asciz foo;
const #26 = Asciz ()Ljava/lang/String;;
const #27 = Asciz s;
const #28 = Asciz Ljava/lang/String;;
const #29 = Asciz main;
const #30 = Asciz ([Ljava/lang/String;)V;
const #31 = Asciz i;
const #32 = Asciz I;
const #33 = Asciz args;
const #34 = Asciz [Ljava/lang/String;;
const #35 = Asciz StackMapTable;
const #36 = class #62; // java/lang/String
const #37 = Asciz Exceptions;
const #38 = class #63; // java/lang/Exception
const #39 = Asciz SourceFile;
const #40 = Asciz MultipleUse.java;
const #41 = NameAndType #18:#19;// "<init>":()V
const #42 = Asciz testing;
const #43 = Asciz java/lang/StringBuilder;
const #44 = NameAndType #18:#64;// "<init>":(Ljava/lang/String;)V
const #45 = NameAndType #65:#66;// append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
const #46 = NameAndType #67:#26;// toString:()Ljava/lang/String;
const #47 = Asciz testingtestingtestingtesting;
const #48 = NameAndType #25:#26;// foo:()Ljava/lang/String;
const #49 = class #62; // java/lang/String
const #50 = NameAndType #68:#69;// equals:(Ljava/lang/Object;)Z
const #51 = class #70; // java/lang/System
const #52 = NameAndType #71:#72;// out:Ljava/io/PrintStream;
const #53 = Asciz bad string concat;
const #54 = class #73; // java/io/PrintStream
const #55 = NameAndType #74:#64;// println:(Ljava/lang/String;)V
const #56 = Asciz done;
const #57 = NameAndType #75:#76;// in:Ljava/io/InputStream;
const #58 = class #77; // java/io/InputStream
const #59 = NameAndType #78:#79;// read:()I
const #60 = Asciz MultipleUse;
const #61 = Asciz java/lang/Object;
const #62 = Asciz java/lang/String;
const #63 = Asciz java/lang/Exception;
const #64 = Asciz (Ljava/lang/String;)V;
const #65 = Asciz append;
const #66 = Asciz (Ljava/lang/String;)Ljava/lang/StringBuilder;;
const #67 = Asciz toString;
const #68 = Asciz equals;
const #69 = Asciz (Ljava/lang/Object;)Z;
const #70 = Asciz java/lang/System;
const #71 = Asciz out;
const #72 = Asciz Ljava/io/PrintStream;;
const #73 = Asciz java/io/PrintStream;
const #74 = Asciz println;
const #75 = Asciz in;
const #76 = Asciz Ljava/io/InputStream;;
const #77 = Asciz java/io/InputStream;
const #78 = Asciz read;
const #79 = Asciz ()I;
{
public MultipleUse();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LMultipleUse;
public static java.lang.String foo();
Code:
Stack=3, Locals=1, Args_size=0
0: ldc #2; //String testing
2: astore_0
3: new #3; //class java/lang/StringBuilder
6: dup
7: aload_0
8: invokespecial #4; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
11: aload_0
12: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: invokevirtual #6; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
18: astore_0
19: new #3; //class java/lang/StringBuilder
22: dup
23: aload_0
24: invokespecial #4; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
27: aload_0
28: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: invokevirtual #6; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: astore_0
35: aload_0
36: areturn
LineNumberTable:
line 3: 0
line 4: 3
line 5: 19
line 6: 35
LocalVariableTable:
Start Length Slot Name Signature
3 34 0 s Ljava/lang/String;
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
Stack=2, Locals=3, Args_size=1
0: ldc #2; //String testing
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: sipush 20000
9: if_icmpge 37
12: ldc #7; //String testingtestingtestingtesting
14: invokestatic #8; //Method foo:()Ljava/lang/String;
17: invokevirtual #9; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
20: ifne 31
23: getstatic #10; //Field java/lang/System.out:Ljava/io/PrintStream;
26: ldc #11; //String bad string concat
28: invokevirtual #12; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
31: iinc 2, 1
34: goto 5
37: getstatic #10; //Field java/lang/System.out:Ljava/io/PrintStream;
40: ldc #13; //String done
42: invokevirtual #12; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: getstatic #14; //Field java/lang/System.in:Ljava/io/InputStream;
48: invokevirtual #15; //Method java/io/InputStream.read:()I
51: pop
52: return
LineNumberTable:
line 10: 0
line 11: 3
line 12: 12
line 13: 23
line 11: 31
line 16: 37
line 17: 45
line 18: 52
LocalVariableTable:
Start Length Slot Name Signature
5 32 2 i I
0 53 0 args [Ljava/lang/String;
3 50 1 s Ljava/lang/String;
StackMapTable: number_of_entries = 3
frame_type = 253 /* append */
offset_delta = 5
locals = [ class java/lang/String, int ]
frame_type = 25 /* same */
frame_type = 250 /* chop */
offset_delta = 5
Exceptions:
throws java.lang.Exception
}
$
OptimizeStringConcat
// PhaseStringOpts::collect_toString_calls
//
// Find all StringBuffer.toString() and StringBuilder.toString() calls,
// by scanning all control-flow nodes, starting from Root
// PhaseStringOpts::build_candidate
//
// Iterate through all calls related to string concats,
// starting from the toString() call, following up by use-def
//
// During iteration:
// 1. Find all append(int)/append(char)/append(String) calls,
// record the arguments and their concat modes;
// append(Integer.toString(i)) calls are treated as append(i).
// If any calls to the SB's other methods are found, give up.
// 2. Find the Allocate node of this StringBuffer/StringBuilder;
// give up if not found or if the shape looks strange.
// 3. Find the constructor call by following the def-use chain of the allocation;
// look for <init>()V/<init>(int)V/<init>(String)V versions;
// give up if no such constructor call is found,
// or if the input to <init>(String)V is proven to be null.
// Collect the toString call, all append calls, the constructor call and the
// allocation node in _control.
//
// After all above calls are found, call validate_control_flow();
// return the StringConcat if control-flow is good; return NULL otherwise
// StringConcat::validate_control_flow
// Give up if too many uncommon traps hit at _begin's bci.
//
// Search _control and append all known to-be-eliminated nodes onto it.
// Known nodes include the Initialize node from Allocate, and its control projection,
// and all calls control projection/Catch projection.
//
// Step backwards from the toString() call, following the control-flow,
// and look for unexpected control-flow.
// Null-check of the return of append() calls can be eliminated, which is added
// to _control;
// ditto for a test that leads to an uncommon trap;
// other tests are considered unexpected;
// Simple diamonds that represent simple data merges are allowed;
// all other control-flow shapes are considered unexpected, e.g. no loops allowed.
//
// Collect all the results produced by calls in the region.
// Give up when extra uses of the SB is found. E.g. https://gist.github.com/2994300
// Try to iteratively coalesce separate concats.
// Replace the toString result with the all the arguments that made up the other StringConcat.
toStrings = collect_toString_calls()
foreach(n : toStrings) {
sc = build_candidate(n)
}
// sc1 = second group of strcat
sc1 = StringConcat {
_begin: bci 19,
_end: bci 31,
_arguments: {
0: result of first group of strcat,
1: result of first group of strcat
},
_mode: {
0: StringNullCheckMode,
1: StringMode
},
_multiple = false
};
// sc2 = first group of strcat
sc2 = StringConcat {
_begin: bci 3,
_end: bci 15,
_arguments: {
0: s,
1: s
}
_mode: {
0: StringNullCheckMode,
1: StringMode
},
_multiple = false
};
// sc3 = merge(sc1, sc2);
sc3 = StringConcat {
_begin: bci 3,
_end: bci 31,
_arguments: {
0: s,
1: s,
2: s,
3: s
}
_mode: {
0: StringNullCheckMode,
1: StringMode
2: StringNullCheckMode,
3: StringMode
},
_multiple = true
};
String.length() on a String constant could be folded away in later optimization. When all String arguments of a StringConcat are constants, the length of the new char array could be calculated statically, so we'd be allocating the result char array with a fixed size, as in this Gist's test case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment