Kxwutc_tREo5V42!%H#fxf)+KX-$%@!>aJtulcbWRK}rYYtq#t_>kRw>pcHYT(VCi1a5JWf^Cgc$ot-9Wqa3QL?(S8)P$OkI7z>os*N0vzBAZ70R8J
zdn6Cb>&pAe=g6OsACO;AP*iYLNLDzaa8+SWk)~*;7^hgOcu{dyiK?_#iLF$sbV=!j
zvZS)Ta)NTXaJv4B8eJ_~tyHZ?ZC+hT-Ag@J
zy-EF%23~`%5vy@nqfcW|Q%f^QbGK%P=8TrKmb+HAR+HAaHd)(NJ4L%z`@RlFhprQ^
zb5!T9F05;&8>?HXdq)q}GuLD59n~9Jg-(EjMze#_>fM&4HAkUz~
z;FY1eVW{B&!#=}hBQv7}qmxFDjVZ<+#(Bow#($aUnlMc&P42HIuXb6zeRb#RH>P@~
zQKrXCADB_iyv%l+^_YDzw=n0Lx0ugbs9Qu>R9TGCCFnl%z4RNFXiEpn?Uv^)KU$ev
zrCFV|dTp(59dF%eJ+nq@P1Kr`YbI?}Z5TE+HoUdUYa`YkU(2&qv1QoS+CH;Wvt!xS
z*-hK)*vHv7+rM%!a^O0&IV?I_Ic{~l?1VVEIu$w%I#ZkjoGYB4x~RIuxHP%UyPCP?
zxL$TcyLq}Da2s=1aA&$VxX*i7cx?6P^(1-vc~*EnTc^8@yRO>{_VVyL=rz7xV?Aem
zhd1zc_de+T#7D~~#pkyTm<>J~DmOg$HTKQ*?enAhh5I%7z4v$U-|zo8Ksz8kpeIl?
zFf6bka52a!s3eFNY#6*X_*RH)2s@-R6dM{CS{J&o(Rt&cjZcbuM)`&14phndrptCG7jcVesk)|ze5w#aQg+ts$`Z=c%Xy5sat!p``exATnh
zO7hf~zXCHr1
z<66^Rt65ui0&^nc#PgGZC;Lv(Pc_uZ))m!#spr)5PWztjZLnx)Xq0c<-vl?MH_bMO
zHQ)W&>F3TC{g#?mT5Hi6a3=lCi(evs89nQHw&xuET+6Q-zgD$T+X~yE_ATx69kCs}
z&XCTbE|0FBZtL#0-wb}MKd*AW@`B`r{TB%r^DnMk%D%L4Ir;M3m6$7&J>fl%uLfVe
z-@BoA@S5kf8`oW~U+uH+yL7|m#`%7${;r$!n;o~zZ?zAY4Yb`hyWKWuKG=T8;!fvX
z%e&n}Ylbck+YVp3=XCG-efRsfM%IrEjRuT97z-QYJzzbUd6@9<&7+J*OOJPqqsI%M
zh&?HLD*v?hcb(r`dFH$e6HXHY&-|V}o@7qG{3G>`&r|u&$3u;
ziWsDDt34S$WyATC@12+#7(E%}Gn12{W(21eWhUliR;8x6B$gzGr4|)u=I2!uFfcGM
I78Fzf061YUN&o-=
literal 0
HcmV?d00001
diff --git a/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/null_valid.ser b/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/null_valid.ser
new file mode 100644
index 0000000000000000000000000000000000000000..b32c838c80ad94b311dcd5af73d74c1ad0ec9dd6
GIT binary patch
literal 7030
zcmeHMXHXQ`8ok{;IS^)G2$FLU1SAX~IVZ_cRE8PC5C$BgU_=&IUxKzEQuMOnpFL`CJz=&RbduipE&_5Qr+s_uLG^jF{a-R@h}r_OEt9iUUV
zKt7I<&d_IMr0O%15|X(3ZVnEceYi={?1U&k7yERacu(bEBL)T_lM7`3d+bnf>8YDT
zuj~;3xdBBslljja{!v^uBY~Y2#d2h%GJ>MGDeNRpW-1Yfc!PcG8>#mgTmVl{
zg3rrN3C;v&VhU^kKq4oV%Wp#|Bs5G3I{`>Q4p0CV&=|~=WG{c`0Dc}Ih!u=~J>E?K
zNWk3I`_Iq+8vSo~L{=h)#cvVE=i^vWDNH_2<>OdZB1^#Ydjzo=smXi}cI0CTF26xO
z4iVtUZ#Y4KW4_^CUtM*xUv
z2_~V$=A=e(IgAADujJo(`IlNEDS{UsnZ`~?)njx1&LaF=|EoHJ_42LtH&$QQ0YoeR
zlS|@%1Nut;`$D02G5mpc2%8de8*gz-iD0&Vq~J
z3b+n#fpPEv@W3p10T#eJuna*E79vAbNCr}b)FEBS2%9P$DlLNdFV291G)o!89k1kMZd#fFf@!N#th?%3B|-=vN462Bbau~1SgMz1V%zr!c8Jh
zqCnzDi4lnfNs6SgWRPU0WR>JONgfTMY0%tg3A7^GY1#zsqm;aqom7<6KB+dTF{vf#
zRnoT7QPTO+?b74YA7zweoMhr<4#;%LJd{Pr>d5-aX2>3uy(~MwN@A7eD(0&ERi{_o
zmxJYWlAl+gDcCB+DU>SoDaC@m<aul@=#H*NuQp%JSzWh!Qjer(ryfl^BqT=C{BS+l}?YGRh(m-TbvhMOkA>E
zF1VsyJzNjCPP)mvG2NQn7TnF;x4RE`kUac7Dm@;1>UeTJd%a*Ucdvt9Q|r~&bJllx
z18+C)gWeB)G<~-C{Nju8_3^Fped1^6x6N@9ZDPw}=h+VIO7`nG*SMOvrFif7#`u+l;Dq)>Tw+9G4~N2u=L{q%BxNRz
zChI2WCG)meY&o=Lf$PC-NP$u|rgW!D@PEw1Xr&p!F&j`vmlPR9b$sFFQ
zvvuFrr&-QfC$dr5%y>^ixduseSDjU0oV
zlAO1Dg7@_1%IEINo%zn?yOunWJZ|3PUdz2T-=n@~e?PKs?Y@eAU-DV`*9r^@$_hRe
zG7GQ$VE9A%4=ekl_m32r79B0d6myCv4%i$xSt4GNRWftX>tJW8QfYqa+e4cV4V4*}
z)f^@qPCLvi_bl(OP^&1e_*5BNIbLO7)m|-Ey{~%d2~7O*t8J&X7n}m8(oQ}9DdMMz(;lb$&(P1b{jC0TbqBR0zZ2@*+PTmb+r{e+
z?H=uM@9FQg?CtnP|Ch$I%4e(2NuDd}BlP9=t(?y~zjz_}!u-XUi!=S3`yX5ixpZg1
zcVOhQ$K|V6oUdFOv>QBs)#~cmA&a4&YxHYf*G;c?4x0>j+%UP(F=9H>dDHA>_bu~V
zy`$?!`^Id>F5Y&$edUhZo$KT4$44guC+<#$Px9`v?moMhaPRf~^!v*Xc1@wD@*j#l
zEPEvPsP0$oU)y=6ymQlz)5DMbA3vC3&b;_7<+o3>xlhPX%AP7dZF*+(?ChNL+^y#u
zp3lBWez82C_q+J-)i1SP{`|`J)$l^d!t87A>yslKG5F)t<&00*PvxJrKlgs|_%gMUxUwSD0zxey)B-{+Ak+dvEg;kaLM3u;
ziWsDDt34S$WyATC@12+#7(E%}Gn12{W(21eWhUliR;8x6B$gzGr4|)u=I2!uFfcGM
z765G*1e=(Xn3t~SnOBmUo?0aIXu*>2jqMgpAcI)T5_3vZfi?>u)cKX>CZ!g&t(hvz
RJ>@|+$YBMZ)0i$*002rXN~r(<
literal 0
HcmV?d00001
diff --git a/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/valid.ser b/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/valid.ser
new file mode 100644
index 0000000000000000000000000000000000000000..a6512d244cf246a938ee37fe376ee7c92bd57160
GIT binary patch
literal 140
zcmZ4UmVvdnh(R_hu`E$9vAjetIX@@ANYB&RIX<8$KP@vSHOSqmj6-netmDhsm>3u;
ziWsDDt34S$WyATC@12+#7(E%}Gn12{W(21eWhUliR;8x6B$gzGr4|)u=I2!uFfcGM
PmN2k82ger!ZK(hNwt_Jz
literal 0
HcmV?d00001
diff --git a/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/valid_invalid.ser b/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/valid_invalid.ser
new file mode 100644
index 0000000000000000000000000000000000000000..dbe11f2c6d651974009b5c4d96db2f45923a47dc
GIT binary patch
literal 141
zcmZ4UmVvdnh(R_hu`E$9vAjetIX@@ANYB&RIX<8$KP@vSHOSqmj6-netmDhsm>3u;
ziWsDDt34S$WyATC@12+#7(E%}Gn12{W(21eWhUliR;8x6B$gzGr4|)u=I2!uFfcGM
QmN2k82ger!Z7HY#0O%4im;e9(
literal 0
HcmV?d00001
diff --git a/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/valid_null.ser b/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/valid_null.ser
new file mode 100644
index 0000000000000000000000000000000000000000..dbe11f2c6d651974009b5c4d96db2f45923a47dc
GIT binary patch
literal 141
zcmZ4UmVvdnh(R_hu`E$9vAjetIX@@ANYB&RIX<8$KP@vSHOSqmj6-netmDhsm>3u;
ziWsDDt34S$WyATC@12+#7(E%}Gn12{W(21eWhUliR;8x6B$gzGr4|)u=I2!uFfcGM
QmN2k82ger!Z7HY#0O%4im;e9(
literal 0
HcmV?d00001
diff --git a/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/valid_valid.ser b/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/valid_valid.ser
new file mode 100644
index 0000000000000000000000000000000000000000..fbe90d0eb6ea8059d4cde7ff1b8e31d7531919b7
GIT binary patch
literal 7039
zcmeHMXHXQ`8ok{;IS^)G$T@>V2?I#ZNpcj0VTLe-0fs1;WpM>p5D`g=AcBMiSC_bo
ziUBd8pnw70K^75p6<0B#@@DW=?b}!H{o8tf-gH&>J^l5m@B41uTh*t|ZT}shGq^w@
zk&(?XVB};PFjJFLxdt9iPEmf`)Hrr>Y=E0%Hcq^+dZ-x#1CYxFvj3es99n+j=I|>=
z1VA1@iOppGGe=-7m(56K=f$#|8JUcbSZ)S8m6MwW0Dv+9)+sP5gI^_!3*dE*?Qf($
zU~oY^K`lN(c1mz3GgC5PBLGr3nOr_oIV?Ov89M<;Kps#47SI^Xj5MD>*C2jEAm9qd
zzh3Vq03?Xq*8k7n|H}TiHzF&A!{Yaf
Y*0sC#VlP4-G@3&^>4xnuq>?KEVh~f+b)DSQ9pc>97Or1qZ?F
z;dnS5&Vvi!y>KPm0Jp>4@Ok(eJPz~V=kOu|5F$cDR1kfHjyNH{NCXmxaFH!Y5mJFP
zARS0QGKAblc*qOnBMO6}qLfkkC~K5EDj3B?rK7f>_Moa!$5Gv=i>TYEY19JhE1Ha!
zN9&=j&>rY8bUZo-U5KtgH=(=Gm(b(rS@b&$21CPWVJtB2m~c!YCLgm0a|qLkIfog=
z%wQI=SgZ_I4{L+<#WJv&*g|X-wjFyGJBFRbe!vlNN;ngoD=q@Z!R^3R;97A5xG~%u
zZV4}j*T7rheeo>(Mtljr5#Nu$iJ!$U5yS~v1RFvSA(611P)X<@Tp~;n-VljIRiY))
zpU5U|BUTbm5U&uYi62Q6k}k=K6iLb=?Iksn&XMks-jK;;O|m^Xf}Ba-OKu@wAU`61
z5TS|~hA+|-VMyyY4
zQfyJ2DsC+9E1n|0N4!;hSo|pkOVOmbP*{`#%2CP%$_y2us!^S&OzKW*BlQw>RstiT
zCE+2FC{Zl&qr`~Bf+R)KR5C;|SF%R(j3keS&@^ctv}9Td?Idl2_DM=X%0VhtYPVE}
z)R@$JX*p?o=~(F^=}zf!=}$7sGA=SnGW%qDWFE?*WOZfzWpiW?%U+UwAtxbcEyt8A
zk~=APUmlj%l@E~5mp>|hU4BtPQNc|iO`%HRg2D?$nxeg8qGGw?fa1IoRmoO~tyHdb
zR_U3tq_Tr@vT~*JMdbw*c@=k+ER}kd5tSuXE!AMv9jcwG57h{2bhS9Oa}tz6Lf2IZ|cE%=6Y{E>K4%!wHA|f3A!JB5B-WI+S1W-tL16S
zk5=YZnN}yQURmo~Ct0^zKV74>CU#BZni(5a8-`824bN8DHrn=xEzeHHj$zke_t;*|
zo@L);Kj)z1km%6v@Y2!9k?Yvy_}-OGKS`=p102h*d)W5LtHbF=3~FOpZFSGCt;Z(VP$cfSwpf*gZNf*u5G2WJHj
zhKPnlhO~sd4|NVL3+06whHVbJ7A_mk4)2Y?MubE(MJ%p!S$AOFY@}IaVdTVmt@XL<
zhd0P>NZxQJN;HZQbt)Pi9U9#l{h8s*XkaYHc*NAkEHIsz2bs@V_N;Q&e5_4uS?pY#
zO;FW64(YWC|y_r$uy_es7<%}LA2p~;;oxRmIWJ`RPG#JQNN
zn3|h9nx>aln8r)DNX4hnY$O*|gl`Edh
z$-TZ&cjNAjb9t_L$MRA6%>2PkDx0=%n%?ZNxqb_@C1%UuR<*4KTW7bqZ9Bf5usvz}
zjU7fi%67cn8M?E-K%rn;!OVAV-?bHr6mknEcUkYM`yTZ@`}>jIt9MuJ{#wK;x>{^h
zTv7aa4|C7eAB=yf{9$=-+}@EA^OD1*m{LyZ#6G)y$IHaa^2%oR`|R&7S1vCqe|sS6
zz;K0WMcqNd!K{P4O7F_vD)p+;s?XI4)#EjeHJ!EcwYzKIA7ULEJ8XZrqfVi&xbD*t
z_K~~wZuQ*_nhh04F-NnHK4}bT96Cln*3u-~RNVBXnbXWW9&r3(i$zOIt9)xo8{C%F
zHs2oEe(Oi)AA39WJL)@Woy8}>iL4XPev1BS;-uHf!Bg~89Y1UQT-!zMD(Z&1H+C=d
zB=qol!+S^jJo^Uwt^2!vG5DqVw94t4Gm>XY1_%QM1IuUg&MuxyJNM#z{P~%|sKEyp
z!Y0ztkzN_@BJ=e^ybze8T-gU$5M%Re>NcT;P
zo4vOzZ}pF^866n28#{m7`S#^I9(S&duN@zq2%fk*89B+j%ewpYUh=)y_p|RWJ=ivd
zo+^4M_ORlS{G*0nb$;#Sne)y}J5OJK9QgRb40GoBZyCRRo-KGneo`@~G}rRfeW_61hXZun{d5h#MAlK=$e1K@oFfU0T$G~@W^
I3z51108jusz5oCK
literal 0
HcmV?d00001
diff --git a/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/valid_wrongType.ser b/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/valid_wrongType.ser
new file mode 100644
index 0000000000000000000000000000000000000000..3538676276fd1b9cb42cfdd37378f05967885d59
GIT binary patch
literal 217
zcmZ4UmVvdnh(R_hu`E$9vAjetIX@@ANYB&RIX<8$KP@vSHOSqmj6-netmDhsm>3u;
ziWsDDt34S$WyATC@12+#7(E%}Gn12{W(21eWhUliR;8x6B$gzGr4|)u=I2!uFfcGM
zmN2k82ger!Z2>w%5Nvc#VqUtQXI@EadTNo-qXkR8H?~_aflOpAOUx-v1v)_hq0X-~
ZHz~EKZOv3!?kNwtK`t!toW^vi0syHLO!oi)
literal 0
HcmV?d00001
diff --git a/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/wrongType.ser b/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/wrongType.ser
new file mode 100644
index 0000000000000000000000000000000000000000..3469f871ef6042f9d8d244c3ec9388cc3063423a
GIT binary patch
literal 207
zcmZ4UmVvdnh(R_hu`E$9vAjetIX@@ANYB&RIX<8$KP@vSHOSqmj6-netmDhsm>3u;
ziWsDDt34S$WyATC@12+#7(E%}Gn12{W(21eWhUliR;8x6B$gzGr4|)u=I2!uFfcGM
z0xcH=8<>-rm#*iTSCX2ZS|s#n!IJNd?G{WRb6Cp~b4pWz77HNM`IY7-r53fVnJUXY
QM2_;L?iH?cY!43iv&;V3`1#}iWGb1S6H$u`Akj6^K
z@7I?v0K6Lnkd(P=@!#{`BmV{wIcWlpWVtxWd>3w@0`J20OU6T(0cwmmv9(>w(S6@JYkE3+28V^qyridfILtH
zsz3|q0~25YY=I+i0q(#D1b|==0b;;Pzy`@64G2LF$Oi?W7?gtTU>B$X`$0Wu0L|be
zXai@#C2$qo0C&Iucm%{?47>o-;2oHQAP5VQAsVC#X+wsPIb;hlpv906v;+!+VxV{^
z2@*geC?DDcl|toE4RjD{fPR6_K$oB%s1JGojX;yoU(iPwfl06;tO*;zRxkthfCJ$O
zcmH
zB6UbJ(t&g%cM&l%g?vC^P&AYd$^zwr@<%O0u~Dm0>rrK>8q_gVE2;~17d3*KMtwn(
z(HdxTv@@ECjz%Y;bI`@;O7s!*Y4jEJ0D27l4uiqaF~%52j6Ws@lY&``DZ}i=v|uh`
z`Y@xI87vm7iZ#c&VuP_PY!5NM5L{xqoj+Z2c$P-GTDgiPL3sKk++f?$d}1a
z$nWK7a+Y#TIgZ>0xmvkaxqi8q@_2bec@O!O@_F)=@~7l)%TFob6^s1gE
zC=4mgP-qkzN-!mjQbsvW>7h(eu~Z|f50yhLq8_4NrjF7Onm)~o#-?qe)zhxf#uPD%
z#)?eE6va}-pA~x*r-xk|N4=aj^BglTw#(RkQ|Y~p6J+N93po+-wZ
zVVY{X*Yu7VY-VqkY*uS_+Z;A`Fz1=?HSb%1Uf{Gqu%K?ikOj%Y!y?sLYoeoziiEH*|xQ|_ZE^D`YznCuzlfc
zJ99g(-9Eef_B8t-`_1;9_MaUb9fS_e4wH@sj`5Cr9ETW+j1Wc{hU%k`kEc(Kmn_{9eni`{hHSZ;N0Pu=z1
zIqpZ?$309vQaqYGUV2)43O!GI&U!g}t@FC*jd=Tcmw5O3P<@v9)c8E{)$>jAZS(*ceF>jJt0NrB;kHGxl;m@N@5=?H>@0)lo14KFoZ
zDp=YU41$@#JA)sG7>BG5`7IO^8WLI?`Yg;QEI+I}oEE++{CN1g2+xRZ5s#LcEE6s3
zjFgXzi)@ITjq;8vj}k{)MX!s#5u+Bvi)oL=#zw{-iJe*QvwYX`u{is<;<&*T#w&7H
z^sH1{$zOSHmHaB!s#Edk_^9~f@t;`1tUA_A0yAMx!Zh29y_@}lzT4exb|f6D%p+0@|Fqp4r{QT&!PTv~kE839$0D(Fhr
zO3zL2%P`L<&JeG5UcGDev@lS3BooS9k=dT5DETqp6q$&A5RGIn&aTaVpA(sLDwmQg
z$i2D7Y|WN6<9WV$hu5Okve$Oz>*g2akF4`pcW^zlK4E?52K^018^$*JZ9G;$C`c{1
zRcKvUUifxX)TWLi&7zG(qd)ll&{!;2EG!<{?6P_PkEkDcKlW}}xTSi_ml96N^-}B7
z%F<6|?6T`W+5A-X)7PzuTYI-TY}>aTvt6)#aEIHDW95|cyz~
zfOp{jLBE5obw+iShcJh-4?U}otnWU|INWeV?MUg7&qoDE#mB;qbu~CPG#uABzO4~%
z6g5sZ#Wmge+56}AW{c*7E%cVs6X1mC#PeU`e;GU(c(U^p<5csnhQIDPO*>uE3bn3j
zoo-8R6Sv2-_niqi)7jzDar!sQ-;SQuJzIND>D;#Sg!4t`zh20@Fmo~E;?$+2OQW5u
zIv-t*zI?AMw5#_@;FW7veXm~b_UOKF&Gp*Z9_OAj*BRH_ZaCa%y=i~*^ey{ar+XcG
zTW>qwZolJnr=xFC-}!#G{!4eg?_Ryfymw<@=|JD$vcda9aYN$!ocj|G_zzw`%zilc
zXyY(?xa6_I?F)c*BJ4
z#Mw#T$ve-NKOcLM@nUYO_)p58dtREn{PmUltDDo&(_^oNufP6P_D1#1(YJPQJ7+>>
zp1ezYH#b}IUiJO44~!4pAEQ2w&*gl=eyaLx`nlsv;FsaAXd;ys+AoB%ezJSaZkof{KU*P}P7nnc0k*EPgst67Z0T8hkfF-v8sICFP
LC{c305SRNe4T)CE
literal 0
HcmV?d00001
diff --git a/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/wrongType_null.ser b/test/jdk/java/awt/color/ICC_Profile/Serialization/SerializationSpecTest/wrongType_null.ser
new file mode 100644
index 0000000000000000000000000000000000000000..2c1536cc09423dfa9117647e3d5206ba5763c15b
GIT binary patch
literal 208
zcmZ4UmVvdnh(R_hu`E$9vAjetIX@@ANYB&RIX<8$KP@vSHOSqmj6-netmDhsm>3u;
ziWsDDt34S$WyATC@12+#7(E%}Gn12{W(21eWhUliR;8x6B$gzGr4|)u=I2!uFfcGM
z0xcH=8<>-rm#*iTSCX2ZS|s#n!IJNd?G{WRb6Cp~b4pWz77HNM`IY7-r53fVnJUXY
R3u;
ziWsDDt34S$WyATC@12+#7(E%}Gn12{W(21eWhUliR;8x6B$gzGr4|)u=I2!uFfcGM
z0xcH=8<>-rm#*iTSCX2ZS|s#n!IJNd?G{WRb6Cp~b4pWz77HNM`IY7-r53fVnJUXY
R3u;
ziWsDDt34S$WyATC@12+#7(E%}Gn12{W(21eWhUliR;8x6B$gzGr4|)u=I2!uFfcGM
z0xcH=8<>-rm#*iTSCX2ZS|s#n!IJNd?G{WRb6Cp~b4pWz77HNM`IY7-r53fVnJUXY
X
Date: Fri, 3 Oct 2025 18:45:34 +0000
Subject: [PATCH 017/160] 8356202: Cleanup Source code in String Implementation
Classes
Reviewed-by: jpai, rgiulietti, liach
---
.../share/classes/java/lang/StringLatin1.java | 124 +++++---------
.../share/classes/java/lang/StringUTF16.java | 161 +++++++++---------
2 files changed, 127 insertions(+), 158 deletions(-)
diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java
index da3acbe5f0a..61c62d049bc 100644
--- a/src/java.base/share/classes/java/lang/StringLatin1.java
+++ b/src/java.base/share/classes/java/lang/StringLatin1.java
@@ -41,61 +41,49 @@ import static java.lang.String.checkIndex;
import static java.lang.String.checkOffset;
final class StringLatin1 {
- public static char charAt(byte[] value, int index) {
+ static char charAt(byte[] value, int index) {
checkIndex(index, value.length);
return (char)(value[index] & 0xff);
}
- public static boolean canEncode(char cp) {
+ static boolean canEncode(char cp) {
return cp <= 0xff;
}
- public static boolean canEncode(int cp) {
+ static boolean canEncode(int cp) {
return cp >=0 && cp <= 0xff;
}
- public static byte coderFromChar(char cp) {
+ static byte coderFromChar(char cp) {
return (byte)((0xff - cp) >>> (Integer.SIZE - 1));
}
- public static int length(byte[] value) {
+ static int length(byte[] value) {
return value.length;
}
- public static int codePointAt(byte[] value, int index, int end) {
- return value[index] & 0xff;
- }
-
- public static int codePointBefore(byte[] value, int index) {
- return value[index - 1] & 0xff;
- }
-
- public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
- return endIndex - beginIndex;
- }
-
- public static char[] toChars(byte[] value) {
+ static char[] toChars(byte[] value) {
char[] dst = new char[value.length];
inflate(value, 0, dst, 0, value.length);
return dst;
}
- public static byte[] inflate(byte[] value, int off, int len) {
+ static byte[] inflate(byte[] value, int off, int len) {
byte[] ret = StringUTF16.newBytesFor(len);
inflate(value, off, ret, 0, len);
return ret;
}
- public static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
+ static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
- public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte[] dst, int dstBegin) {
+ static void getBytes(byte[] value, int srcBegin, int srcEnd, byte[] dst, int dstBegin) {
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
@IntrinsicCandidate
- public static boolean equals(byte[] value, byte[] other) {
+ static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) {
@@ -108,20 +96,20 @@ final class StringLatin1 {
}
@IntrinsicCandidate
- public static int compareTo(byte[] value, byte[] other) {
+ static int compareTo(byte[] value, byte[] other) {
int len1 = value.length;
int len2 = other.length;
return compareTo(value, other, len1, len2);
}
- public static int compareTo(byte[] value, byte[] other, int len1, int len2) {
+ static int compareTo(byte[] value, byte[] other, int len1, int len2) {
int lim = Math.min(len1, len2);
int k = ArraysSupport.mismatch(value, other, lim);
return (k < 0) ? len1 - len2 : getChar(value, k) - getChar(other, k);
}
@IntrinsicCandidate
- public static int compareToUTF16(byte[] value, byte[] other) {
+ static int compareToUTF16(byte[] value, byte[] other) {
int len1 = length(value);
int len2 = StringUTF16.length(other);
return compareToUTF16Values(value, other, len1, len2);
@@ -130,7 +118,7 @@ final class StringLatin1 {
/*
* Checks the boundary and then compares the byte arrays.
*/
- public static int compareToUTF16(byte[] value, byte[] other, int len1, int len2) {
+ static int compareToUTF16(byte[] value, byte[] other, int len1, int len2) {
checkOffset(len1, length(value));
checkOffset(len2, StringUTF16.length(other));
@@ -149,7 +137,7 @@ final class StringLatin1 {
return len1 - len2;
}
- public static int compareToCI(byte[] value, byte[] other) {
+ static int compareToCI(byte[] value, byte[] other) {
int len1 = value.length;
int len2 = other.length;
int lim = Math.min(len1, len2);
@@ -169,7 +157,7 @@ final class StringLatin1 {
return len1 - len2;
}
- public static int compareToCI_UTF16(byte[] value, byte[] other) {
+ static int compareToCI_UTF16(byte[] value, byte[] other) {
int len1 = length(value);
int len2 = StringUTF16.length(other);
int lim = Math.min(len1, len2);
@@ -191,12 +179,12 @@ final class StringLatin1 {
return len1 - len2;
}
- public static int hashCode(byte[] value) {
+ static int hashCode(byte[] value) {
return ArraysSupport.hashCodeOfUnsigned(value, 0, value.length, 0);
}
// Caller must ensure that from- and toIndex are within bounds
- public static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) {
+ static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) {
if (!canEncode(ch)) {
return -1;
}
@@ -215,7 +203,7 @@ final class StringLatin1 {
}
@IntrinsicCandidate
- public static int indexOf(byte[] value, byte[] str) {
+ static int indexOf(byte[] value, byte[] str) {
if (str.length == 0) {
return 0;
}
@@ -226,7 +214,7 @@ final class StringLatin1 {
}
@IntrinsicCandidate
- public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
+ static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
byte first = str[0];
int max = (valueCount - strCount);
for (int i = fromIndex; i <= max; i++) {
@@ -248,8 +236,8 @@ final class StringLatin1 {
return -1;
}
- public static int lastIndexOf(byte[] src, int srcCount,
- byte[] tgt, int tgtCount, int fromIndex) {
+ static int lastIndexOf(byte[] src, int srcCount,
+ byte[] tgt, int tgtCount, int fromIndex) {
int min = tgtCount - 1;
int i = min + fromIndex;
int strLastIndex = tgtCount - 1;
@@ -276,7 +264,7 @@ final class StringLatin1 {
}
}
- public static int lastIndexOf(final byte[] value, int ch, int fromIndex) {
+ static int lastIndexOf(final byte[] value, int ch, int fromIndex) {
if (!canEncode(ch)) {
return -1;
}
@@ -289,7 +277,7 @@ final class StringLatin1 {
return -1;
}
- public static String replace(byte[] value, char oldChar, char newChar) {
+ static String replace(byte[] value, char oldChar, char newChar) {
if (canEncode(oldChar)) {
int len = value.length;
int i = -1;
@@ -326,8 +314,8 @@ final class StringLatin1 {
return null; // for string to return this;
}
- public static String replace(byte[] value, int valLen, byte[] targ,
- int targLen, byte[] repl, int replLen)
+ static String replace(byte[] value, int valLen, byte[] targ,
+ int targLen, byte[] repl, int replLen)
{
assert targLen > 0;
int i, j, p = 0;
@@ -377,8 +365,8 @@ final class StringLatin1 {
}
// case insensitive
- public static boolean regionMatchesCI(byte[] value, int toffset,
- byte[] other, int ooffset, int len) {
+ static boolean regionMatchesCI(byte[] value, int toffset,
+ byte[] other, int ooffset, int len) {
int last = toffset + len;
while (toffset < last) {
byte b1 = value[toffset++];
@@ -391,8 +379,8 @@ final class StringLatin1 {
return true;
}
- public static boolean regionMatchesCI_UTF16(byte[] value, int toffset,
- byte[] other, int ooffset, int len) {
+ static boolean regionMatchesCI_UTF16(byte[] value, int toffset,
+ byte[] other, int ooffset, int len) {
int last = toffset + len;
while (toffset < last) {
char c1 = (char)(value[toffset++] & 0xff);
@@ -413,7 +401,7 @@ final class StringLatin1 {
return true;
}
- public static String toLowerCase(String str, byte[] value, Locale locale) {
+ static String toLowerCase(String str, byte[] value, Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
@@ -480,7 +468,7 @@ final class StringLatin1 {
return StringUTF16.newString(result, 0, resultOffset);
}
- public static String toUpperCase(String str, byte[] value, Locale locale) {
+ static String toUpperCase(String str, byte[] value, Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
@@ -560,7 +548,7 @@ final class StringLatin1 {
return StringUTF16.newString(result, 0, resultOffset);
}
- public static String trim(byte[] value) {
+ static String trim(byte[] value) {
int len = value.length;
int st = 0;
while ((st < len) && ((value[st] & 0xff) <= ' ')) {
@@ -573,7 +561,7 @@ final class StringLatin1 {
newString(value, st, len - st) : null;
}
- public static int indexOfNonWhitespace(byte[] value) {
+ static int indexOfNonWhitespace(byte[] value) {
int length = value.length;
int left = 0;
while (left < length) {
@@ -586,9 +574,8 @@ final class StringLatin1 {
return left;
}
- public static int lastIndexOfNonWhitespace(byte[] value) {
- int length = value.length;
- int right = length;
+ static int lastIndexOfNonWhitespace(byte[] value) {
+ int right = value.length;
while (0 < right) {
char ch = getChar(value, right - 1);
if (ch != ' ' && ch != '\t' && !CharacterDataLatin1.instance.isWhitespace(ch)) {
@@ -599,7 +586,7 @@ final class StringLatin1 {
return right;
}
- public static String strip(byte[] value) {
+ static String strip(byte[] value) {
int left = indexOfNonWhitespace(value);
if (left == value.length) {
return "";
@@ -609,12 +596,12 @@ final class StringLatin1 {
return ifChanged ? newString(value, left, right - left) : null;
}
- public static String stripLeading(byte[] value) {
+ static String stripLeading(byte[] value) {
int left = indexOfNonWhitespace(value);
return (left != 0) ? newString(value, left, value.length - left) : null;
}
- public static String stripTrailing(byte[] value) {
+ static String stripTrailing(byte[] value) {
int right = lastIndexOfNonWhitespace(value);
return (right != value.length) ? newString(value, 0, right) : null;
}
@@ -713,14 +700,14 @@ final class StringLatin1 {
return StreamSupport.stream(LinesSpliterator.spliterator(value), false);
}
- public static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) {
+ static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) {
value[i] = (byte)c1;
value[i + 1] = (byte)c2;
value[i + 2] = (byte)c3;
value[i + 3] = (byte)c4;
}
- public static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) {
+ static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) {
value[i] = (byte)c1;
value[i + 1] = (byte)c2;
value[i + 2] = (byte)c3;
@@ -728,32 +715,15 @@ final class StringLatin1 {
value[i + 4] = (byte)c5;
}
- public static void putChar(byte[] val, int index, int c) {
- //assert (canEncode(c));
- val[index] = (byte)(c);
- }
-
- public static char getChar(byte[] val, int index) {
+ static char getChar(byte[] val, int index) {
return (char)(val[index] & 0xff);
}
- public static byte[] toBytes(int[] val, int off, int len) {
- byte[] ret = new byte[len];
- for (int i = 0; i < len; i++) {
- int cp = val[off++];
- if (!canEncode(cp)) {
- return null;
- }
- ret[i] = (byte)cp;
- }
- return ret;
- }
-
- public static byte[] toBytes(char c) {
+ static byte[] toBytes(char c) {
return new byte[] { (byte)c };
}
- public static String newString(byte[] val, int index, int len) {
+ static String newString(byte[] val, int index, int len) {
if (len == 0) {
return "";
}
@@ -763,7 +733,7 @@ final class StringLatin1 {
// inflatedCopy byte[] -> char[]
@IntrinsicCandidate
- public static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
+ static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
for (int i = 0; i < len; i++) {
dst[dstOff++] = (char)(src[srcOff++] & 0xff);
}
@@ -771,7 +741,7 @@ final class StringLatin1 {
// inflatedCopy byte[] -> byte[]
@IntrinsicCandidate
- public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
+ static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
StringUTF16.inflate(src, srcOff, dst, dstOff, len);
}
@@ -824,7 +794,7 @@ final class StringLatin1 {
}
@Override
- public long estimateSize() { return (long)(fence - index); }
+ public long estimateSize() { return fence - index; }
@Override
public int characteristics() {
diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java
index 08b4072fc35..4e31c9728e9 100644
--- a/src/java.base/share/classes/java/lang/StringUTF16.java
+++ b/src/java.base/share/classes/java/lang/StringUTF16.java
@@ -54,13 +54,13 @@ final class StringUTF16 {
// Return a new byte array for a UTF16-coded string for len chars
// Throw an exception if out of range
- public static byte[] newBytesFor(int len) {
+ static byte[] newBytesFor(int len) {
return new byte[newBytesLength(len)];
}
// Check the size of a UTF16-coded string
// Throw an exception if out of range
- public static int newBytesLength(int len) {
+ static int newBytesLength(int len) {
if (len < 0) {
throw new NegativeArraySizeException();
}
@@ -89,7 +89,7 @@ final class StringUTF16 {
((val[index] & 0xff) << LO_BYTE_SHIFT));
}
- public static int length(byte[] value) {
+ static int length(byte[] value) {
return value.length >> 1;
}
@@ -111,7 +111,7 @@ final class StringUTF16 {
return c1;
}
- public static int codePointAt(byte[] value, int index, int end) {
+ static int codePointAt(byte[] value, int index, int end) {
return codePointAt(value, index, end, false /* unchecked */);
}
@@ -134,7 +134,7 @@ final class StringUTF16 {
return c2;
}
- public static int codePointBefore(byte[] value, int index) {
+ static int codePointBefore(byte[] value, int index) {
return codePointBefore(value, index, false /* unchecked */);
}
@@ -155,11 +155,11 @@ final class StringUTF16 {
return count;
}
- public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
+ static int codePointCount(byte[] value, int beginIndex, int endIndex) {
return codePointCount(value, beginIndex, endIndex, false /* unchecked */);
}
- public static char[] toChars(byte[] value) {
+ static char[] toChars(byte[] value) {
char[] dst = new char[value.length >> 1];
getChars(value, 0, dst.length, dst, 0);
return dst;
@@ -173,7 +173,7 @@ final class StringUTF16 {
* @param len a length
*/
@IntrinsicCandidate
- public static byte[] toBytes(char[] value, int off, int len) {
+ static byte[] toBytes(char[] value, int off, int len) {
byte[] val = newBytesFor(len);
for (int i = 0; i < len; i++) {
putChar(val, i, value[off]);
@@ -218,7 +218,7 @@ final class StringUTF16 {
* @param count count of chars to be compressed, {@code count} > 0
*/
@ForceInline
- public static byte[] compress(final char[] val, final int off, final int count) {
+ static byte[] compress(final char[] val, final int off, final int count) {
byte[] latin1 = new byte[count];
int ndx = compress(val, off, latin1, 0, count);
if (ndx != count) {
@@ -245,7 +245,7 @@ final class StringUTF16 {
* @param off starting offset
* @param count count of chars to be compressed, {@code count} > 0
*/
- public static byte[] compress(final byte[] val, final int off, final int count) {
+ static byte[] compress(final byte[] val, final int off, final int count) {
byte[] latin1 = new byte[count];
int ndx = compress(val, off, latin1, 0, count);
if (ndx != count) {// Switch to UTF16
@@ -279,7 +279,7 @@ final class StringUTF16 {
* @param off starting offset
* @param count length of code points to be compressed, length > 0
*/
- public static byte[] compress(final int[] val, int off, final int count) {
+ static byte[] compress(final int[] val, int off, final int count) {
// Optimistically copy all latin1 code points to the destination
byte[] latin1 = new byte[count];
final int end = off + count;
@@ -389,7 +389,7 @@ final class StringUTF16 {
// compressedCopy char[] -> byte[]
@IntrinsicCandidate
- public static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) {
+ static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) {
for (int i = 0; i < len; i++) {
char c = src[srcOff];
if (c > 0xff) {
@@ -404,7 +404,7 @@ final class StringUTF16 {
// compressedCopy byte[] -> byte[]
@IntrinsicCandidate
- public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
+ static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
// We need a range check here because 'getChar' has no checks
checkBoundsOffCount(srcOff, len, src);
for (int i = 0; i < len; i++) {
@@ -420,7 +420,7 @@ final class StringUTF16 {
}
// Create the UTF16 buffer for !COMPACT_STRINGS
- public static byte[] toBytes(int[] val, int index, int len) {
+ static byte[] toBytes(int[] val, int index, int len) {
final int end = index + len;
int n = computeCodePointSize(val, index, end);
@@ -428,7 +428,7 @@ final class StringUTF16 {
return extractCodepoints(val, index, end, buf, 0);
}
- public static byte[] toBytes(char c) {
+ static byte[] toBytes(char c) {
byte[] result = new byte[2];
putChar(result, 0, c);
return result;
@@ -442,7 +442,7 @@ final class StringUTF16 {
}
@IntrinsicCandidate
- public static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
+ static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
// We need a range check here because 'getChar' has no checks
if (srcBegin < srcEnd) {
checkBoundsOffCount(srcBegin, srcEnd - srcBegin, value);
@@ -453,7 +453,7 @@ final class StringUTF16 {
}
/* @see java.lang.String.getBytes(int, int, byte[], int) */
- public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte[] dst, int dstBegin) {
+ static void getBytes(byte[] value, int srcBegin, int srcEnd, byte[] dst, int dstBegin) {
srcBegin <<= 1;
srcEnd <<= 1;
for (int i = srcBegin + (1 >> LO_BYTE_SHIFT); i < srcEnd; i += 2) {
@@ -462,7 +462,7 @@ final class StringUTF16 {
}
@IntrinsicCandidate
- public static int compareTo(byte[] value, byte[] other) {
+ static int compareTo(byte[] value, byte[] other) {
int len1 = length(value);
int len2 = length(other);
return compareValues(value, other, len1, len2);
@@ -471,7 +471,7 @@ final class StringUTF16 {
/*
* Checks the boundary and then compares the byte arrays.
*/
- public static int compareTo(byte[] value, byte[] other, int len1, int len2) {
+ static int compareTo(byte[] value, byte[] other, int len1, int len2) {
checkOffset(len1, value);
checkOffset(len2, other);
@@ -491,15 +491,15 @@ final class StringUTF16 {
}
@IntrinsicCandidate
- public static int compareToLatin1(byte[] value, byte[] other) {
+ static int compareToLatin1(byte[] value, byte[] other) {
return -StringLatin1.compareToUTF16(other, value);
}
- public static int compareToLatin1(byte[] value, byte[] other, int len1, int len2) {
+ static int compareToLatin1(byte[] value, byte[] other, int len1, int len2) {
return -StringLatin1.compareToUTF16(other, value, len2, len1);
}
- public static int compareToCI(byte[] value, byte[] other) {
+ static int compareToCI(byte[] value, byte[] other) {
return compareToCIImpl(value, 0, length(value), other, 0, length(other));
}
@@ -512,8 +512,8 @@ final class StringUTF16 {
assert olast <= length(other);
for (int k1 = toffset, k2 = ooffset; k1 < tlast && k2 < olast; k1++, k2++) {
- int cp1 = (int)getChar(value, k1);
- int cp2 = (int)getChar(other, k2);
+ int cp1 = getChar(value, k1);
+ int cp2 = getChar(other, k2);
if (cp1 == cp2 || compareCodePointCI(cp1, cp2) == 0) {
continue;
@@ -588,16 +588,16 @@ final class StringUTF16 {
return cp;
}
- public static int compareToCI_Latin1(byte[] value, byte[] other) {
+ static int compareToCI_Latin1(byte[] value, byte[] other) {
return -StringLatin1.compareToCI_UTF16(other, value);
}
- public static int hashCode(byte[] value) {
+ static int hashCode(byte[] value) {
return ArraysSupport.hashCodeOfUTF16(value, 0, value.length >> 1, 0);
}
// Caller must ensure that from- and toIndex are within bounds
- public static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) {
+ static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) {
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
@@ -608,7 +608,7 @@ final class StringUTF16 {
}
@IntrinsicCandidate
- public static int indexOf(byte[] value, byte[] str) {
+ static int indexOf(byte[] value, byte[] str) {
if (str.length == 0) {
return 0;
}
@@ -619,7 +619,7 @@ final class StringUTF16 {
}
@IntrinsicCandidate
- public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
+ static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
checkBoundsBeginEnd(fromIndex, valueCount, value);
checkBoundsBeginEnd(0, strCount, str);
return indexOfUnsafe(value, valueCount, str, strCount, fromIndex);
@@ -657,7 +657,7 @@ final class StringUTF16 {
* Handles indexOf Latin1 substring in UTF16 string.
*/
@IntrinsicCandidate
- public static int indexOfLatin1(byte[] value, byte[] str) {
+ static int indexOfLatin1(byte[] value, byte[] str) {
if (str.length == 0) {
return 0;
}
@@ -668,13 +668,13 @@ final class StringUTF16 {
}
@IntrinsicCandidate
- public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) {
+ static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) {
checkBoundsBeginEnd(fromIndex, srcCount, src);
String.checkBoundsBeginEnd(0, tgtCount, tgt.length);
return indexOfLatin1Unsafe(src, srcCount, tgt, tgtCount, fromIndex);
}
- public static int indexOfLatin1Unsafe(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) {
+ static int indexOfLatin1Unsafe(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) {
assert fromIndex >= 0;
assert tgtCount > 0;
assert tgtCount <= tgt.length;
@@ -730,8 +730,8 @@ final class StringUTF16 {
}
// srcCoder == UTF16 && tgtCoder == UTF16
- public static int lastIndexOf(byte[] src, int srcCount,
- byte[] tgt, int tgtCount, int fromIndex) {
+ static int lastIndexOf(byte[] src, int srcCount,
+ byte[] tgt, int tgtCount, int fromIndex) {
assert fromIndex >= 0;
assert tgtCount > 0;
assert tgtCount <= length(tgt);
@@ -765,7 +765,7 @@ final class StringUTF16 {
}
}
- public static int lastIndexOf(byte[] value, int ch, int fromIndex) {
+ static int lastIndexOf(byte[] value, int ch, int fromIndex) {
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
@@ -798,7 +798,7 @@ final class StringUTF16 {
return -1;
}
- public static String replace(byte[] value, char oldChar, char newChar) {
+ static String replace(byte[] value, char oldChar, char newChar) {
int len = value.length >> 1;
int i = -1;
while (++i < len) {
@@ -829,9 +829,9 @@ final class StringUTF16 {
return null;
}
- public static String replace(byte[] value, int valLen, boolean valLat1,
- byte[] targ, int targLen, boolean targLat1,
- byte[] repl, int replLen, boolean replLat1)
+ static String replace(byte[] value, int valLen, boolean valLat1,
+ byte[] targ, int targLen, boolean targLat1,
+ byte[] repl, int replLen, boolean replLat1)
{
assert targLen > 0;
assert !valLat1 || !targLat1 || !replLat1;
@@ -944,18 +944,18 @@ final class StringUTF16 {
return new String(result, UTF16);
}
- public static boolean regionMatchesCI(byte[] value, int toffset,
- byte[] other, int ooffset, int len) {
+ static boolean regionMatchesCI(byte[] value, int toffset,
+ byte[] other, int ooffset, int len) {
return compareToCIImpl(value, toffset, len, other, ooffset, len) == 0;
}
- public static boolean regionMatchesCI_Latin1(byte[] value, int toffset,
- byte[] other, int ooffset,
- int len) {
+ static boolean regionMatchesCI_Latin1(byte[] value, int toffset,
+ byte[] other, int ooffset,
+ int len) {
return StringLatin1.regionMatchesCI_UTF16(other, ooffset, value, toffset, len);
}
- public static String toLowerCase(String str, byte[] value, Locale locale) {
+ static String toLowerCase(String str, byte[] value, Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
@@ -965,7 +965,7 @@ final class StringUTF16 {
// Now check if there are any characters that need to be changed, or are surrogate
for (first = 0 ; first < len; first++) {
- int cp = (int)getChar(value, first);
+ int cp = getChar(value, first);
if (Character.isSurrogate((char)cp)) {
hasSurr = true;
break;
@@ -988,7 +988,7 @@ final class StringUTF16 {
}
int bits = 0;
for (int i = first; i < len; i++) {
- int cp = (int)getChar(value, i);
+ int cp = getChar(value, i);
if (cp == '\u03A3' || // GREEK CAPITAL LETTER SIGMA
Character.isSurrogate((char)cp)) {
return toLowerCaseEx(str, value, result, i, locale, false);
@@ -1003,7 +1003,7 @@ final class StringUTF16 {
bits |= cp;
putChar(result, i, cp);
}
- if (bits < 0 || bits > 0xff) {
+ if (bits > 0xff) {
return new String(result, UTF16);
} else {
return newString(result, 0, len);
@@ -1059,7 +1059,7 @@ final class StringUTF16 {
return newString(result, 0, resultOffset);
}
- public static String toUpperCase(String str, byte[] value, Locale locale) {
+ static String toUpperCase(String str, byte[] value, Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
@@ -1069,7 +1069,7 @@ final class StringUTF16 {
// Now check if there are any characters that need to be changed, or are surrogate
for (first = 0 ; first < len; first++) {
- int cp = (int)getChar(value, first);
+ int cp = getChar(value, first);
if (Character.isSurrogate((char)cp)) {
hasSurr = true;
break;
@@ -1093,7 +1093,7 @@ final class StringUTF16 {
}
int bits = 0;
for (int i = first; i < len; i++) {
- int cp = (int)getChar(value, i);
+ int cp = getChar(value, i);
if (Character.isSurrogate((char)cp)) {
return toUpperCaseEx(str, value, result, i, locale, false);
}
@@ -1104,7 +1104,7 @@ final class StringUTF16 {
bits |= cp;
putChar(result, i, cp);
}
- if (bits < 0 || bits > 0xff) {
+ if (bits > 0xff) {
return new String(result, UTF16);
} else {
return newString(result, 0, len);
@@ -1164,7 +1164,7 @@ final class StringUTF16 {
return newString(result, 0, resultOffset);
}
- public static String trim(byte[] value) {
+ static String trim(byte[] value) {
int length = value.length >> 1;
int len = length;
int st = 0;
@@ -1179,7 +1179,7 @@ final class StringUTF16 {
null;
}
- public static int indexOfNonWhitespace(byte[] value) {
+ static int indexOfNonWhitespace(byte[] value) {
int length = value.length >> 1;
int left = 0;
while (left < length) {
@@ -1192,9 +1192,8 @@ final class StringUTF16 {
return left;
}
- public static int lastIndexOfNonWhitespace(byte[] value) {
- int length = value.length >>> 1;
- int right = length;
+ static int lastIndexOfNonWhitespace(byte[] value) {
+ int right = value.length >>> 1;
while (0 < right) {
int codepoint = codePointBefore(value, right);
if (codepoint != ' ' && codepoint != '\t' && !Character.isWhitespace(codepoint)) {
@@ -1205,7 +1204,7 @@ final class StringUTF16 {
return right;
}
- public static String strip(byte[] value) {
+ static String strip(byte[] value) {
int length = value.length >>> 1;
int left = indexOfNonWhitespace(value);
if (left == length) {
@@ -1216,13 +1215,13 @@ final class StringUTF16 {
return ifChanged ? newString(value, left, right - left) : null;
}
- public static String stripLeading(byte[] value) {
+ static String stripLeading(byte[] value) {
int length = value.length >>> 1;
int left = indexOfNonWhitespace(value);
return (left != 0) ? newString(value, left, length - left) : null;
}
- public static String stripTrailing(byte[] value) {
+ static String stripTrailing(byte[] value) {
int length = value.length >>> 1;
int right = lastIndexOfNonWhitespace(value);
return (right != length) ? newString(value, 0, right) : null;
@@ -1322,7 +1321,7 @@ final class StringUTF16 {
return StreamSupport.stream(LinesSpliterator.spliterator(value), false);
}
- public static String newString(byte[] val, int index, int len) {
+ static String newString(byte[] val, int index, int len) {
if (len == 0) {
return "";
}
@@ -1388,7 +1387,7 @@ final class StringUTF16 {
}
@Override
- public long estimateSize() { return (long)(fence - index); }
+ public long estimateSize() { return fence - index; }
@Override
public int characteristics() {
@@ -1473,7 +1472,7 @@ final class StringUTF16 {
}
@Override
- public long estimateSize() { return (long)(fence - index); }
+ public long estimateSize() { return fence - index; }
@Override
public int characteristics() {
@@ -1483,12 +1482,12 @@ final class StringUTF16 {
////////////////////////////////////////////////////////////////
- public static void putCharSB(byte[] val, int index, int c) {
+ static void putCharSB(byte[] val, int index, int c) {
checkIndex(index, val);
putChar(val, index, c);
}
- public static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) {
+ static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) {
checkBoundsBeginEnd(index, index + end - off, val);
String.checkBoundsBeginEnd(off, end, ca.length);
Unsafe.getUnsafe().copyMemory(
@@ -1499,26 +1498,26 @@ final class StringUTF16 {
(long) (end - off) << 1);
}
- public static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) {
+ static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) {
checkBoundsBeginEnd(index, index + end - off, val);
for (int i = off; i < end; i++) {
putChar(val, index++, s.charAt(i));
}
}
- public static int codePointAtSB(byte[] val, int index, int end) {
+ static int codePointAtSB(byte[] val, int index, int end) {
return codePointAt(val, index, end, true /* checked */);
}
- public static int codePointBeforeSB(byte[] val, int index) {
+ static int codePointBeforeSB(byte[] val, int index) {
return codePointBefore(val, index, true /* checked */);
}
- public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) {
+ static int codePointCountSB(byte[] val, int beginIndex, int endIndex) {
return codePointCount(val, beginIndex, endIndex, true /* checked */);
}
- public static boolean contentEquals(byte[] v1, byte[] v2, int len) {
+ static boolean contentEquals(byte[] v1, byte[] v2, int len) {
checkBoundsOffCount(0, len, v2);
for (int i = 0; i < len; i++) {
if ((char)(v1[i] & 0xff) != getChar(v2, i)) {
@@ -1528,7 +1527,7 @@ final class StringUTF16 {
return true;
}
- public static boolean contentEquals(byte[] value, CharSequence cs, int len) {
+ static boolean contentEquals(byte[] value, CharSequence cs, int len) {
checkOffset(len, value);
for (int i = 0; i < len; i++) {
if (getChar(value, i) != cs.charAt(i)) {
@@ -1538,7 +1537,7 @@ final class StringUTF16 {
return true;
}
- public static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) {
+ static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) {
int end = i + 4;
checkBoundsBeginEnd(i, end, value);
putChar(value, i, c1);
@@ -1547,7 +1546,7 @@ final class StringUTF16 {
putChar(value, i + 3, c4);
}
- public static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) {
+ static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) {
int end = i + 5;
checkBoundsBeginEnd(i, end, value);
putChar(value, i, c1);
@@ -1557,12 +1556,12 @@ final class StringUTF16 {
putChar(value, i + 4, c5);
}
- public static char charAt(byte[] value, int index) {
+ static char charAt(byte[] value, int index) {
checkIndex(index, value);
return getChar(value, index);
}
- public static void reverse(byte[] val, int count) {
+ static void reverse(byte[] val, int count) {
checkOffset(count, val);
int n = count - 1;
boolean hasSurrogates = false;
@@ -1597,7 +1596,7 @@ final class StringUTF16 {
}
// inflatedCopy byte[] -> byte[]
- public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
+ static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
// We need a range check here because 'putChar' has no checks
checkBoundsOffCount(dstOff, len, dst);
for (int i = 0; i < len; i++) {
@@ -1606,7 +1605,7 @@ final class StringUTF16 {
}
// srcCoder == UTF16 && tgtCoder == LATIN1
- public static int lastIndexOfLatin1(byte[] src, int srcCount,
+ static int lastIndexOfLatin1(byte[] src, int srcCount,
byte[] tgt, int tgtCount, int fromIndex) {
assert fromIndex >= 0;
assert tgtCount > 0;
@@ -1659,19 +1658,19 @@ final class StringUTF16 {
static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
- public static void checkIndex(int off, byte[] val) {
+ static void checkIndex(int off, byte[] val) {
String.checkIndex(off, length(val));
}
- public static void checkOffset(int off, byte[] val) {
+ static void checkOffset(int off, byte[] val) {
String.checkOffset(off, length(val));
}
- public static void checkBoundsBeginEnd(int begin, int end, byte[] val) {
+ static void checkBoundsBeginEnd(int begin, int end, byte[] val) {
String.checkBoundsBeginEnd(begin, end, length(val));
}
- public static void checkBoundsOffCount(int offset, int count, byte[] val) {
+ static void checkBoundsOffCount(int offset, int count, byte[] val) {
String.checkBoundsOffCount(offset, count, length(val));
}
From 837f634bf29fd877dd62a2e0f7d7a1bd383372d3 Mon Sep 17 00:00:00 2001
From: "Daniel D. Daugherty"
Date: Fri, 3 Oct 2025 21:11:33 +0000
Subject: [PATCH 018/160] 8369128: ProblemList
jdk/jfr/event/profiling/TestCPUTimeSampleQueueAutoSizes.java in Xcomp configs
8369132: Disable vmTestbase/gc/vector/CircularListLow and LinearListLow with
SerialGC 8369133: Disable gc/g1/TestShrinkAuxiliaryDataRunner.java with
UseLargePages option
Reviewed-by: ayang, dholmes
---
test/hotspot/jtreg/gc/g1/TestShrinkAuxiliaryDataRunner.java | 1 +
.../vmTestbase/gc/vector/CircularListLow/TestDescription.java | 1 +
.../vmTestbase/gc/vector/LinearListLow/TestDescription.java | 1 +
test/jdk/ProblemList-Xcomp.txt | 1 +
4 files changed, 4 insertions(+)
diff --git a/test/hotspot/jtreg/gc/g1/TestShrinkAuxiliaryDataRunner.java b/test/hotspot/jtreg/gc/g1/TestShrinkAuxiliaryDataRunner.java
index 309ba722787..d478f811490 100644
--- a/test/hotspot/jtreg/gc/g1/TestShrinkAuxiliaryDataRunner.java
+++ b/test/hotspot/jtreg/gc/g1/TestShrinkAuxiliaryDataRunner.java
@@ -29,6 +29,7 @@ package gc.g1;
* @bug 8038423 8061715
* @summary Checks that decommitment occurs for JVM with different ObjectAlignmentInBytes values
* @requires vm.gc.G1
+ * @requires !vm.opt.final.UseLargePages
* @library /test/lib
* @library /
* @modules java.base/jdk.internal.misc
diff --git a/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java
index 68fe6779e99..252e3a2d40f 100644
--- a/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java
+++ b/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java
@@ -31,5 +31,6 @@
*
* @library /vmTestbase
* /test/lib
+ * @requires vm.gc != "Serial"
* @run main/othervm/timeout=480 gc.vector.SimpleGC.SimpleGC -ms low -gp circularList(low)
*/
diff --git a/test/hotspot/jtreg/vmTestbase/gc/vector/LinearListLow/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/vector/LinearListLow/TestDescription.java
index 8ae86af035d..bd094045abf 100644
--- a/test/hotspot/jtreg/vmTestbase/gc/vector/LinearListLow/TestDescription.java
+++ b/test/hotspot/jtreg/vmTestbase/gc/vector/LinearListLow/TestDescription.java
@@ -31,5 +31,6 @@
*
* @library /vmTestbase
* /test/lib
+ * @requires vm.gc != "Serial"
* @run main/othervm gc.vector.SimpleGC.SimpleGC -ms low -gp linearList(low)
*/
diff --git a/test/jdk/ProblemList-Xcomp.txt b/test/jdk/ProblemList-Xcomp.txt
index 5ed171a1fea..8e37e6e15d0 100644
--- a/test/jdk/ProblemList-Xcomp.txt
+++ b/test/jdk/ProblemList-Xcomp.txt
@@ -29,3 +29,4 @@
java/lang/invoke/MethodHandles/CatchExceptionTest.java 8146623 generic-all
java/lang/reflect/callerCache/ReflectionCallerCacheTest.java 8332028 generic-all
+jdk/jfr/event/profiling/TestCPUTimeSampleQueueAutoSizes.java 8367302 linux-all
From e6868c624851d5c6bd182e45ba908cb06b731e8c Mon Sep 17 00:00:00 2001
From: "Daniel D. Daugherty"
Date: Fri, 3 Oct 2025 22:17:01 +0000
Subject: [PATCH 019/160] 8369138: New test
compiler/loopstripmining/MissingStoreAfterOuterStripMinedLoop.java fails
Reviewed-by: kvn
---
.../MissingStoreAfterOuterStripMinedLoop.java | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/test/hotspot/jtreg/compiler/loopstripmining/MissingStoreAfterOuterStripMinedLoop.java b/test/hotspot/jtreg/compiler/loopstripmining/MissingStoreAfterOuterStripMinedLoop.java
index 8fa6cae12e6..24e3c4a5d30 100644
--- a/test/hotspot/jtreg/compiler/loopstripmining/MissingStoreAfterOuterStripMinedLoop.java
+++ b/test/hotspot/jtreg/compiler/loopstripmining/MissingStoreAfterOuterStripMinedLoop.java
@@ -29,7 +29,8 @@
* leading to wrong results.
*
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation
- * -Xcomp -XX:-UseLoopPredicate -XX:-UseAutoVectorizationPredicate
+ * -Xcomp -XX:-UseLoopPredicate
+ * -XX:+UnlockDiagnosticVMOptions -XX:-UseAutoVectorizationPredicate
* -XX:CompileCommand=compileonly,compiler.loopstripmining.MissingStoreAfterOuterStripMinedLoop::test*
* compiler.loopstripmining.MissingStoreAfterOuterStripMinedLoop
* @run main compiler.loopstripmining.MissingStoreAfterOuterStripMinedLoop
@@ -133,4 +134,4 @@ public class MissingStoreAfterOuterStripMinedLoop {
throw new RuntimeException("unexpected value: " + a1.field + " " + a2.field + " " + a3.field);
}
}
-}
\ No newline at end of file
+}
From c3fbbfabcc9a9535a3b422c1c9afaa8e092a5da0 Mon Sep 17 00:00:00 2001
From: Sergey Bylokhov
Date: Fri, 3 Oct 2025 23:16:41 +0000
Subject: [PATCH 020/160] 8369027: Apply java.io.Serial annotations in
java.scripting
Reviewed-by: rriggs
---
.../share/classes/javax/script/ScriptException.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/java.scripting/share/classes/javax/script/ScriptException.java b/src/java.scripting/share/classes/javax/script/ScriptException.java
index 1037ccde27f..48fb3221914 100644
--- a/src/java.scripting/share/classes/javax/script/ScriptException.java
+++ b/src/java.scripting/share/classes/javax/script/ScriptException.java
@@ -25,6 +25,8 @@
package javax.script;
+import java.io.Serial;
+
/**
* The generic Exception class for the Scripting APIs. Checked
* exception types thrown by underlying scripting implementations must be wrapped in instances of
@@ -36,6 +38,7 @@ package javax.script;
*/
public class ScriptException extends Exception {
+ @Serial
private static final long serialVersionUID = 8265071037049225001L;
/** @serial */
From 76dba201fa1a525780677e4d3dee8e9ffafd1cd7 Mon Sep 17 00:00:00 2001
From: Jaikiran Pai
Date: Sat, 4 Oct 2025 08:09:09 +0000
Subject: [PATCH 021/160] 8368821: Test
java/net/httpclient/http3/GetHTTP3Test.java intermittently fails with
java.io.IOException: QUIC endpoint closed
Reviewed-by: dfuchs
---
test/jdk/java/net/httpclient/http3/GetHTTP3Test.java | 12 ++++++------
.../jdk/java/net/httpclient/http3/PostHTTP3Test.java | 10 +++++-----
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/test/jdk/java/net/httpclient/http3/GetHTTP3Test.java b/test/jdk/java/net/httpclient/http3/GetHTTP3Test.java
index a67710c485f..14842d401fd 100644
--- a/test/jdk/java/net/httpclient/http3/GetHTTP3Test.java
+++ b/test/jdk/java/net/httpclient/http3/GetHTTP3Test.java
@@ -33,7 +33,6 @@ import java.net.http.HttpOption.Http3DiscoveryMode;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.StandardCharsets;
-import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -47,6 +46,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import javax.net.ssl.SSLContext;
+import jdk.test.lib.Utils;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.httpclient.test.lib.common.HttpServerAdapters;
import jdk.httpclient.test.lib.http2.Http2TestServer;
@@ -74,8 +74,9 @@ import static java.lang.System.out;
* @library /test/lib /test/jdk/java/net/httpclient/lib
* @build jdk.test.lib.net.SimpleSSLContext
* jdk.httpclient.test.lib.common.HttpServerAdapters
+ * jdk.test.lib.Utils
* @compile ../ReferenceTracker.java
- * @run testng/othervm/timeout=60 -Djdk.internal.httpclient.debug=true
+ * @run testng/othervm -Djdk.internal.httpclient.debug=true
* -Djdk.httpclient.HttpClient.log=requests,responses,errors
* GetHTTP3Test
* @summary Basic HTTP/3 GET test
@@ -216,7 +217,6 @@ public class GetHTTP3Test implements HttpServerAdapters {
.proxy(HttpClient.Builder.NO_PROXY)
.executor(executor)
.sslContext(sslContext)
- .connectTimeout(Duration.ofSeconds(10))
.build();
return TRACKER.track(client);
}
@@ -348,7 +348,7 @@ public class GetHTTP3Test implements HttpServerAdapters {
var tracker = TRACKER.getTracker(client);
client = null;
System.gc();
- AssertionError error = TRACKER.check(tracker, 1000);
+ AssertionError error = TRACKER.check(tracker, Utils.adjustTimeout(1000));
if (error != null) throw error;
}
System.out.println("test: DONE");
@@ -394,7 +394,7 @@ public class GetHTTP3Test implements HttpServerAdapters {
var tracker = TRACKER.getTracker(client);
client = null;
System.gc();
- AssertionError error = TRACKER.check(tracker, 1000);
+ AssertionError error = TRACKER.check(tracker, Utils.adjustTimeout(1000));
if (error != null) throw error;
}
@@ -423,7 +423,7 @@ public class GetHTTP3Test implements HttpServerAdapters {
sharedClient == null ? null : sharedClient.toString();
sharedClient = null;
Thread.sleep(100);
- AssertionError fail = TRACKER.check(500);
+ AssertionError fail = TRACKER.check(Utils.adjustTimeout(1000));
try {
h3TestServer.stop();
} finally {
diff --git a/test/jdk/java/net/httpclient/http3/PostHTTP3Test.java b/test/jdk/java/net/httpclient/http3/PostHTTP3Test.java
index 0e7a57ca699..8bb6515d4ff 100644
--- a/test/jdk/java/net/httpclient/http3/PostHTTP3Test.java
+++ b/test/jdk/java/net/httpclient/http3/PostHTTP3Test.java
@@ -38,7 +38,6 @@ import java.net.http.HttpOption.Http3DiscoveryMode;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.StandardCharsets;
-import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -53,6 +52,7 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
import javax.net.ssl.SSLContext;
+import jdk.test.lib.Utils;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.httpclient.test.lib.common.HttpServerAdapters;
import jdk.httpclient.test.lib.http2.Http2TestServer;
@@ -79,6 +79,7 @@ import static java.lang.System.out;
* @library /test/lib /test/jdk/java/net/httpclient/lib
* @build jdk.test.lib.net.SimpleSSLContext
* jdk.httpclient.test.lib.common.HttpServerAdapters
+ * jdk.test.lib.Utils
* @compile ../ReferenceTracker.java
* @run testng/othervm -Djdk.internal.httpclient.debug=true
* -Djdk.httpclient.HttpClient.log=requests,responses,errors
@@ -221,7 +222,6 @@ public class PostHTTP3Test implements HttpServerAdapters {
.proxy(HttpClient.Builder.NO_PROXY)
.executor(executor)
.sslContext(sslContext)
- .connectTimeout(Duration.ofSeconds(10))
.build();
return TRACKER.track(client);
}
@@ -375,7 +375,7 @@ public class PostHTTP3Test implements HttpServerAdapters {
var tracker = TRACKER.getTracker(client);
client = null;
System.gc();
- AssertionError error = TRACKER.check(tracker, 1000);
+ AssertionError error = TRACKER.check(tracker, Utils.adjustTimeout(1000));
if (error != null) throw error;
}
System.out.println("test: DONE");
@@ -437,7 +437,7 @@ public class PostHTTP3Test implements HttpServerAdapters {
var tracker = TRACKER.getTracker(client);
client = null;
System.gc();
- AssertionError error = TRACKER.check(tracker, 1000);
+ AssertionError error = TRACKER.check(tracker, Utils.adjustTimeout(1000));
if (error != null) throw error;
}
@@ -466,7 +466,7 @@ public class PostHTTP3Test implements HttpServerAdapters {
sharedClient == null ? null : sharedClient.toString();
sharedClient = null;
Thread.sleep(100);
- AssertionError fail = TRACKER.check(500);
+ AssertionError fail = TRACKER.check(Utils.adjustTimeout(1000));
try {
h3TestServer.stop();
} finally {
From f740cd2aad43a008da1ed1ff15ebe2c790f893a0 Mon Sep 17 00:00:00 2001
From: Chad Rakoczy
Date: Sat, 4 Oct 2025 21:17:26 +0000
Subject: [PATCH 022/160] 8316694: Implement relocation of nmethod within
CodeCache
Reviewed-by: kvn, eosterlund, never, eastigeevich, bulasevich
---
src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp | 1 -
src/hotspot/share/code/codeBehaviours.cpp | 11 +-
src/hotspot/share/code/codeBehaviours.hpp | 12 +-
src/hotspot/share/code/codeCache.hpp | 2 +-
src/hotspot/share/code/compiledIC.cpp | 4 +-
src/hotspot/share/code/compiledIC.hpp | 2 +-
src/hotspot/share/code/nmethod.cpp | 311 +++++++++++++++++-
src/hotspot/share/code/nmethod.hpp | 24 +-
src/hotspot/share/code/relocInfo.cpp | 3 +-
src/hotspot/share/compiler/oopMap.cpp | 6 +
src/hotspot/share/compiler/oopMap.hpp | 2 +
.../share/gc/shenandoah/shenandoahUnload.cpp | 2 +-
src/hotspot/share/gc/z/zUnload.cpp | 2 +-
src/hotspot/share/jvmci/jvmciRuntime.hpp | 5 +
src/hotspot/share/prims/whitebox.cpp | 58 +++-
src/hotspot/share/runtime/globals.hpp | 3 +
.../classes/sun/jvm/hotspot/code/NMethod.java | 43 +--
.../whitebox/CompilerWhiteBoxTest.java | 56 +++-
.../whitebox/DeoptimizeRelocatedNMethod.java | 157 +++++++++
.../compiler/whitebox/RelocateNMethod.java | 146 ++++++++
.../RelocateNMethodMultiplePaths.java | 261 +++++++++++++++
.../whitebox/StressNMethodRelocation.java | 239 ++++++++++++++
.../NMethodRelocationTest.java | 178 ++++++++++
.../libNMethodRelocationTest.cpp | 114 +++++++
test/lib/jdk/test/whitebox/WhiteBox.java | 6 +
test/lib/jdk/test/whitebox/code/CodeBlob.java | 7 +-
26 files changed, 1591 insertions(+), 64 deletions(-)
create mode 100644 test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java
create mode 100644 test/hotspot/jtreg/compiler/whitebox/RelocateNMethod.java
create mode 100644 test/hotspot/jtreg/compiler/whitebox/RelocateNMethodMultiplePaths.java
create mode 100644 test/hotspot/jtreg/compiler/whitebox/StressNMethodRelocation.java
create mode 100644 test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java
create mode 100644 test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp
diff --git a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp
index f5d7d9e4387..94694b58d2f 100644
--- a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp
@@ -90,7 +90,6 @@ void Relocation::pd_set_call_destination(address x) {
void trampoline_stub_Relocation::pd_fix_owner_after_move() {
NativeCall* call = nativeCall_at(owner());
- assert(call->raw_destination() == owner(), "destination should be empty");
address trampoline = addr();
address dest = nativeCallTrampolineStub_at(trampoline)->destination();
if (!Assembler::reachable_from_branch_at(owner(), dest)) {
diff --git a/src/hotspot/share/code/codeBehaviours.cpp b/src/hotspot/share/code/codeBehaviours.cpp
index 1f9eb0e2914..bc5a81c95b3 100644
--- a/src/hotspot/share/code/codeBehaviours.cpp
+++ b/src/hotspot/share/code/codeBehaviours.cpp
@@ -23,23 +23,24 @@
*/
#include "code/codeBehaviours.hpp"
+#include "code/nmethod.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
CompiledICProtectionBehaviour* CompiledICProtectionBehaviour::_current = nullptr;
-bool DefaultICProtectionBehaviour::lock(nmethod* method) {
- if (is_safe(method)) {
+bool DefaultICProtectionBehaviour::lock(nmethod* nm) {
+ if (is_safe(nm)) {
return false;
}
CompiledIC_lock->lock_without_safepoint_check();
return true;
}
-void DefaultICProtectionBehaviour::unlock(nmethod* method) {
+void DefaultICProtectionBehaviour::unlock(nmethod* nm) {
CompiledIC_lock->unlock();
}
-bool DefaultICProtectionBehaviour::is_safe(nmethod* method) {
- return SafepointSynchronize::is_at_safepoint() || CompiledIC_lock->owned_by_self();
+bool DefaultICProtectionBehaviour::is_safe(nmethod* nm) {
+ return SafepointSynchronize::is_at_safepoint() || CompiledIC_lock->owned_by_self() || (NMethodState_lock->owned_by_self() && nm->is_not_installed());
}
diff --git a/src/hotspot/share/code/codeBehaviours.hpp b/src/hotspot/share/code/codeBehaviours.hpp
index 0350f5752f6..96a38fffc30 100644
--- a/src/hotspot/share/code/codeBehaviours.hpp
+++ b/src/hotspot/share/code/codeBehaviours.hpp
@@ -33,18 +33,18 @@ class CompiledICProtectionBehaviour {
static CompiledICProtectionBehaviour* _current;
public:
- virtual bool lock(nmethod* method) = 0;
- virtual void unlock(nmethod* method) = 0;
- virtual bool is_safe(nmethod* method) = 0;
+ virtual bool lock(nmethod* nm) = 0;
+ virtual void unlock(nmethod* nm) = 0;
+ virtual bool is_safe(nmethod* nm) = 0;
static CompiledICProtectionBehaviour* current() { return _current; }
static void set_current(CompiledICProtectionBehaviour* current) { _current = current; }
};
class DefaultICProtectionBehaviour: public CompiledICProtectionBehaviour, public CHeapObj {
- virtual bool lock(nmethod* method);
- virtual void unlock(nmethod* method);
- virtual bool is_safe(nmethod* method);
+ virtual bool lock(nmethod* nm);
+ virtual void unlock(nmethod* nm);
+ virtual bool is_safe(nmethod* nm);
};
#endif // SHARE_CODE_CODEBEHAVIOURS_HPP
diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp
index 3e446ab8430..dc9e5d7dc20 100644
--- a/src/hotspot/share/code/codeCache.hpp
+++ b/src/hotspot/share/code/codeCache.hpp
@@ -259,7 +259,7 @@ class CodeCache : AllStatic {
static bool heap_available(CodeBlobType code_blob_type);
// Returns the CodeBlobType for the given nmethod
- static CodeBlobType get_code_blob_type(nmethod* nm) {
+ static CodeBlobType get_code_blob_type(const nmethod* nm) {
return get_code_heap(nm)->code_blob_type();
}
diff --git a/src/hotspot/share/code/compiledIC.cpp b/src/hotspot/share/code/compiledIC.cpp
index 03d9adc8e24..5f5c9711441 100644
--- a/src/hotspot/share/code/compiledIC.cpp
+++ b/src/hotspot/share/code/compiledIC.cpp
@@ -55,8 +55,8 @@ CompiledICLocker::~CompiledICLocker() {
}
}
-bool CompiledICLocker::is_safe(nmethod* method) {
- return CompiledICProtectionBehaviour::current()->is_safe(method);
+bool CompiledICLocker::is_safe(nmethod* nm) {
+ return CompiledICProtectionBehaviour::current()->is_safe(nm);
}
bool CompiledICLocker::is_safe(address code) {
diff --git a/src/hotspot/share/code/compiledIC.hpp b/src/hotspot/share/code/compiledIC.hpp
index 624c1b428de..db8a0860b39 100644
--- a/src/hotspot/share/code/compiledIC.hpp
+++ b/src/hotspot/share/code/compiledIC.hpp
@@ -50,7 +50,7 @@ class CompiledICLocker: public StackObj {
public:
CompiledICLocker(nmethod* method);
~CompiledICLocker();
- static bool is_safe(nmethod* method);
+ static bool is_safe(nmethod* nm);
static bool is_safe(address code);
};
diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp
index 1540fa0333a..7274b627f3e 100644
--- a/src/hotspot/share/code/nmethod.cpp
+++ b/src/hotspot/share/code/nmethod.cpp
@@ -754,7 +754,7 @@ Method* nmethod::attached_method_before_pc(address pc) {
}
void nmethod::clear_inline_caches() {
- assert(SafepointSynchronize::is_at_safepoint(), "clearing of IC's only allowed at safepoint");
+ assert(SafepointSynchronize::is_at_safepoint() || (NMethodState_lock->owned_by_self() && is_not_installed()), "clearing of IC's only allowed at safepoint or when not installed");
RelocIterator iter(this);
while (iter.next()) {
iter.reloc()->clear_inline_cache();
@@ -1146,7 +1146,8 @@ nmethod* nmethod::new_nmethod(const methodHandle& method,
#if INCLUDE_JVMCI
+ align_up(speculations_len , oopSize)
#endif
- + align_up(debug_info->data_size() , oopSize);
+ + align_up(debug_info->data_size() , oopSize)
+ + align_up(ImmutableDataReferencesCounterSize, oopSize);
// First, allocate space for immutable data in C heap.
address immutable_data = nullptr;
@@ -1371,10 +1372,266 @@ nmethod::nmethod(
}
}
+
+nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm._header_size)
+{
+
+ if (nm._oop_maps != nullptr) {
+ _oop_maps = nm._oop_maps->clone();
+ } else {
+ _oop_maps = nullptr;
+ }
+
+ _size = nm._size;
+ _relocation_size = nm._relocation_size;
+ _content_offset = nm._content_offset;
+ _code_offset = nm._code_offset;
+ _data_offset = nm._data_offset;
+ _frame_size = nm._frame_size;
+
+ S390_ONLY( _ctable_offset = nm._ctable_offset; )
+
+ _header_size = nm._header_size;
+ _frame_complete_offset = nm._frame_complete_offset;
+
+ _kind = nm._kind;
+
+ _caller_must_gc_arguments = nm._caller_must_gc_arguments;
+
+#ifndef PRODUCT
+ _asm_remarks.share(nm._asm_remarks);
+ _dbg_strings.share(nm._dbg_strings);
+#endif
+
+ // Allocate memory and copy mutable data to C heap
+ _mutable_data_size = nm._mutable_data_size;
+ if (_mutable_data_size > 0) {
+ _mutable_data = (address)os::malloc(_mutable_data_size, mtCode);
+ if (_mutable_data == nullptr) {
+ vm_exit_out_of_memory(_mutable_data_size, OOM_MALLOC_ERROR, "nmethod: no space for mutable data");
+ }
+ memcpy(mutable_data_begin(), nm.mutable_data_begin(), nm.mutable_data_size());
+ } else {
+ _mutable_data = nullptr;
+ }
+
+ _deoptimization_generation = 0;
+ _gc_epoch = CodeCache::gc_epoch();
+ _method = nm._method;
+ _osr_link = nullptr;
+
+ // Increment number of references to immutable data to share it between nmethods
+ _immutable_data_size = nm._immutable_data_size;
+ if (_immutable_data_size > 0) {
+ _immutable_data = nm._immutable_data;
+ set_immutable_data_references_counter(get_immutable_data_references_counter() + 1);
+ } else {
+ _immutable_data = blob_end();
+ }
+
+ _exception_cache = nullptr;
+ _gc_data = nullptr;
+ _oops_do_mark_nmethods = nullptr;
+ _oops_do_mark_link = nullptr;
+ _compiled_ic_data = nullptr;
+
+ if (nm._osr_entry_point != nullptr) {
+ _osr_entry_point = (nm._osr_entry_point - (address) &nm) + (address) this;
+ } else {
+ _osr_entry_point = nullptr;
+ }
+
+ _entry_offset = nm._entry_offset;
+ _verified_entry_offset = nm._verified_entry_offset;
+ _entry_bci = nm._entry_bci;
+
+ _skipped_instructions_size = nm._skipped_instructions_size;
+ _stub_offset = nm._stub_offset;
+ _exception_offset = nm._exception_offset;
+ _deopt_handler_offset = nm._deopt_handler_offset;
+ _unwind_handler_offset = nm._unwind_handler_offset;
+ _num_stack_arg_slots = nm._num_stack_arg_slots;
+ _oops_size = nm._oops_size;
+#if INCLUDE_JVMCI
+ _metadata_size = nm._metadata_size;
+#endif
+ _nul_chk_table_offset = nm._nul_chk_table_offset;
+ _handler_table_offset = nm._handler_table_offset;
+ _scopes_pcs_offset = nm._scopes_pcs_offset;
+ _scopes_data_offset = nm._scopes_data_offset;
+#if INCLUDE_JVMCI
+ _speculations_offset = nm._speculations_offset;
+#endif
+
+ _orig_pc_offset = nm._orig_pc_offset;
+ _compile_id = nm._compile_id;
+ _comp_level = nm._comp_level;
+ _compiler_type = nm._compiler_type;
+ _is_unloading_state = nm._is_unloading_state;
+ _state = not_installed;
+
+ _has_unsafe_access = nm._has_unsafe_access;
+ _has_wide_vectors = nm._has_wide_vectors;
+ _has_monitors = nm._has_monitors;
+ _has_scoped_access = nm._has_scoped_access;
+ _has_flushed_dependencies = nm._has_flushed_dependencies;
+ _is_unlinked = nm._is_unlinked;
+ _load_reported = nm._load_reported;
+
+ _deoptimization_status = nm._deoptimization_status;
+
+ if (nm._pc_desc_container != nullptr) {
+ _pc_desc_container = new PcDescContainer(scopes_pcs_begin());
+ } else {
+ _pc_desc_container = nullptr;
+ }
+
+ // Copy nmethod contents excluding header
+ // - Constant part (doubles, longs and floats used in nmethod)
+ // - Code part:
+ // - Code body
+ // - Exception handler
+ // - Stub code
+ // - OOP table
+ memcpy(consts_begin(), nm.consts_begin(), nm.data_end() - nm.consts_begin());
+
+ post_init();
+}
+
+nmethod* nmethod::relocate(CodeBlobType code_blob_type) {
+ assert(NMethodRelocation, "must enable use of function");
+
+ // Locks required to be held by caller to ensure the nmethod
+ // is not modified or purged from code cache during relocation
+ assert_lock_strong(CodeCache_lock);
+ assert_lock_strong(Compile_lock);
+ assert(CompiledICLocker::is_safe(this), "mt unsafe call");
+
+ if (!is_relocatable()) {
+ return nullptr;
+ }
+
+ run_nmethod_entry_barrier();
+ nmethod* nm_copy = new (size(), code_blob_type) nmethod(*this);
+
+ if (nm_copy == nullptr) {
+ return nullptr;
+ }
+
+ // Fix relocation
+ RelocIterator iter(nm_copy);
+ CodeBuffer src(this);
+ CodeBuffer dst(nm_copy);
+ while (iter.next()) {
+#ifdef USE_TRAMPOLINE_STUB_FIX_OWNER
+ // Direct calls may no longer be in range and the use of a trampoline may now be required.
+ // Instead, allow trampoline relocations to update their owners and perform the necessary checks.
+ if (iter.reloc()->is_call()) {
+ address trampoline = trampoline_stub_Relocation::get_trampoline_for(iter.reloc()->addr(), nm_copy);
+ if (trampoline != nullptr) {
+ continue;
+ }
+ }
+#endif
+
+ iter.reloc()->fix_relocation_after_move(&src, &dst);
+ }
+
+ // To make dependency checking during class loading fast, record
+ // the nmethod dependencies in the classes it is dependent on.
+ // This allows the dependency checking code to simply walk the
+ // class hierarchy above the loaded class, checking only nmethods
+ // which are dependent on those classes. The slow way is to
+ // check every nmethod for dependencies which makes it linear in
+ // the number of methods compiled. For applications with a lot
+ // classes the slow way is too slow.
+ for (Dependencies::DepStream deps(nm_copy); deps.next(); ) {
+ if (deps.type() == Dependencies::call_site_target_value) {
+ // CallSite dependencies are managed on per-CallSite instance basis.
+ oop call_site = deps.argument_oop(0);
+ MethodHandles::add_dependent_nmethod(call_site, nm_copy);
+ } else {
+ InstanceKlass* ik = deps.context_type();
+ if (ik == nullptr) {
+ continue; // ignore things like evol_method
+ }
+ // record this nmethod as dependent on this klass
+ ik->add_dependent_nmethod(nm_copy);
+ }
+ }
+
+ MutexLocker ml_NMethodState_lock(NMethodState_lock, Mutex::_no_safepoint_check_flag);
+
+ // Verify the nm we copied from is still valid
+ if (!is_marked_for_deoptimization() && is_in_use()) {
+ assert(method() != nullptr && method()->code() == this, "should be if is in use");
+
+ nm_copy->clear_inline_caches();
+
+ // Attempt to start using the copy
+ if (nm_copy->make_in_use()) {
+ ICache::invalidate_range(nm_copy->code_begin(), nm_copy->code_size());
+
+ methodHandle mh(Thread::current(), nm_copy->method());
+ nm_copy->method()->set_code(mh, nm_copy);
+
+ make_not_used();
+
+ nm_copy->post_compiled_method_load_event();
+
+ nm_copy->log_relocated_nmethod(this);
+
+ return nm_copy;
+ }
+ }
+
+ nm_copy->make_not_used();
+
+ return nullptr;
+}
+
+bool nmethod::is_relocatable() {
+ if (!is_java_method()) {
+ return false;
+ }
+
+ if (!is_in_use()) {
+ return false;
+ }
+
+ if (is_osr_method()) {
+ return false;
+ }
+
+ if (is_marked_for_deoptimization()) {
+ return false;
+ }
+
+#if INCLUDE_JVMCI
+ if (jvmci_nmethod_data() != nullptr && jvmci_nmethod_data()->has_mirror()) {
+ return false;
+ }
+#endif
+
+ if (is_unloading()) {
+ return false;
+ }
+
+ if (has_evol_metadata()) {
+ return false;
+ }
+
+ return true;
+}
+
void* nmethod::operator new(size_t size, int nmethod_size, int comp_level) throw () {
return CodeCache::allocate(nmethod_size, CodeCache::get_code_blob_type(comp_level));
}
+void* nmethod::operator new(size_t size, int nmethod_size, CodeBlobType code_blob_type) throw () {
+ return CodeCache::allocate(nmethod_size, code_blob_type);
+}
+
void* nmethod::operator new(size_t size, int nmethod_size, bool allow_NonNMethod_space) throw () {
// Try MethodNonProfiled and MethodProfiled.
void* return_value = CodeCache::allocate(nmethod_size, CodeBlobType::MethodNonProfiled);
@@ -1494,9 +1751,9 @@ nmethod::nmethod(
#if INCLUDE_JVMCI
_speculations_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize);
- DEBUG_ONLY( int immutable_data_end_offset = _speculations_offset + align_up(speculations_len, oopSize); )
+ DEBUG_ONLY( int immutable_data_end_offset = _speculations_offset + align_up(speculations_len, oopSize) + align_up(ImmutableDataReferencesCounterSize, oopSize); )
#else
- DEBUG_ONLY( int immutable_data_end_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize); )
+ DEBUG_ONLY( int immutable_data_end_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize) + align_up(ImmutableDataReferencesCounterSize, oopSize); )
#endif
assert(immutable_data_end_offset <= immutable_data_size, "wrong read-only data size: %d > %d",
immutable_data_end_offset, immutable_data_size);
@@ -1529,6 +1786,7 @@ nmethod::nmethod(
memcpy(speculations_begin(), speculations, speculations_len);
}
#endif
+ set_immutable_data_references_counter(1);
post_init();
@@ -1595,6 +1853,40 @@ void nmethod::log_new_nmethod() const {
}
}
+
+void nmethod::log_relocated_nmethod(nmethod* original) const {
+ if (LogCompilation && xtty != nullptr) {
+ ttyLocker ttyl;
+ xtty->begin_elem("relocated nmethod");
+ log_identity(xtty);
+ xtty->print(" entry='" INTPTR_FORMAT "' size='%d'", p2i(code_begin()), size());
+
+ const char* original_code_heap_name = CodeCache::get_code_heap_name(CodeCache::get_code_blob_type(original));
+ xtty->print(" original_address='" INTPTR_FORMAT "'", p2i(original));
+ xtty->print(" original_code_heap='%s'", original_code_heap_name);
+
+ const char* new_code_heap_name = CodeCache::get_code_heap_name(CodeCache::get_code_blob_type(this));
+ xtty->print(" new_address='" INTPTR_FORMAT "'", p2i(this));
+ xtty->print(" new_code_heap='%s'", new_code_heap_name);
+
+ LOG_OFFSET(xtty, relocation);
+ LOG_OFFSET(xtty, consts);
+ LOG_OFFSET(xtty, insts);
+ LOG_OFFSET(xtty, stub);
+ LOG_OFFSET(xtty, scopes_data);
+ LOG_OFFSET(xtty, scopes_pcs);
+ LOG_OFFSET(xtty, dependencies);
+ LOG_OFFSET(xtty, handler_table);
+ LOG_OFFSET(xtty, nul_chk_table);
+ LOG_OFFSET(xtty, oops);
+ LOG_OFFSET(xtty, metadata);
+
+ xtty->method(method());
+ xtty->stamp();
+ xtty->end_elem();
+ }
+}
+
#undef LOG_OFFSET
@@ -2127,9 +2419,18 @@ void nmethod::purge(bool unregister_nmethod) {
delete[] _compiled_ic_data;
if (_immutable_data != blob_end()) {
- os::free(_immutable_data);
+ int reference_count = get_immutable_data_references_counter();
+ assert(reference_count > 0, "immutable data has no references");
+
+ set_immutable_data_references_counter(reference_count - 1);
+ // Free memory if this is the last nmethod referencing immutable data
+ if (reference_count == 0) {
+ os::free(_immutable_data);
+ }
+
_immutable_data = blob_end(); // Valid not null address
}
+
if (unregister_nmethod) {
Universe::heap()->unregister_nmethod(this);
}
diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp
index 1e876657098..2332766a47c 100644
--- a/src/hotspot/share/code/nmethod.hpp
+++ b/src/hotspot/share/code/nmethod.hpp
@@ -154,6 +154,7 @@ public:
// - Scopes data array
// - Scopes pcs array
// - JVMCI speculations array
+// - Nmethod reference counter
#if INCLUDE_JVMCI
class FailedSpeculation;
@@ -167,6 +168,8 @@ class nmethod : public CodeBlob {
friend class JVMCINMethodData;
friend class DeoptimizationScope;
+ #define ImmutableDataReferencesCounterSize ((int)sizeof(int))
+
private:
// Used to track in which deoptimize handshake this method will be deoptimized.
@@ -330,8 +333,11 @@ class nmethod : public CodeBlob {
#endif
);
+ nmethod(const nmethod &nm);
+
// helper methods
void* operator new(size_t size, int nmethod_size, int comp_level) throw();
+ void* operator new(size_t size, int nmethod_size, CodeBlobType code_blob_type) throw();
// For method handle intrinsics: Try MethodNonProfiled, MethodProfiled and NonNMethod.
// Attention: Only allow NonNMethod space for special nmethods which don't need to be
@@ -564,6 +570,12 @@ public:
#endif
);
+ // Relocate the nmethod to the code heap identified by code_blob_type.
+ // Returns nullptr if the code heap does not have enough space, the
+ // nmethod is unrelocatable, or the nmethod is invalidated during relocation,
+ // otherwise the relocated nmethod. The original nmethod will be marked not entrant.
+ nmethod* relocate(CodeBlobType code_blob_type);
+
static nmethod* new_native_nmethod(const methodHandle& method,
int compile_id,
CodeBuffer *code_buffer,
@@ -580,6 +592,8 @@ public:
bool is_java_method () const { return _method != nullptr && !_method->is_native(); }
bool is_osr_method () const { return _entry_bci != InvocationEntryBci; }
+ bool is_relocatable();
+
// Compiler task identification. Note that all OSR methods
// are numbered in an independent sequence if CICountOSR is true,
// and native method wrappers are also numbered independently if
@@ -632,11 +646,13 @@ public:
#if INCLUDE_JVMCI
address scopes_data_end () const { return _immutable_data + _speculations_offset ; }
address speculations_begin () const { return _immutable_data + _speculations_offset ; }
- address speculations_end () const { return immutable_data_end(); }
+ address speculations_end () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; }
#else
- address scopes_data_end () const { return immutable_data_end(); }
+ address scopes_data_end () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; }
#endif
+ address immutable_data_references_counter_begin () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; }
+
// Sizes
int immutable_data_size() const { return _immutable_data_size; }
int consts_size () const { return int( consts_end () - consts_begin ()); }
@@ -946,6 +962,9 @@ public:
bool load_reported() const { return _load_reported; }
void set_load_reported() { _load_reported = true; }
+ inline int get_immutable_data_references_counter() { return *((int*)immutable_data_references_counter_begin()); }
+ inline void set_immutable_data_references_counter(int count) { *((int*)immutable_data_references_counter_begin()) = count; }
+
public:
// ScopeDesc retrieval operation
PcDesc* pc_desc_at(address pc) { return find_pc_desc(pc, false); }
@@ -1014,6 +1033,7 @@ public:
// Logging
void log_identity(xmlStream* log) const;
void log_new_nmethod() const;
+ void log_relocated_nmethod(nmethod* original) const;
void log_state_change(InvalidationReason invalidation_reason) const;
// Prints block-level comments, including nmethod specific block labels:
diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp
index 8fc22596d01..02a1e5faf16 100644
--- a/src/hotspot/share/code/relocInfo.cpp
+++ b/src/hotspot/share/code/relocInfo.cpp
@@ -406,11 +406,12 @@ void CallRelocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer
pd_set_call_destination(callee);
}
-
#ifdef USE_TRAMPOLINE_STUB_FIX_OWNER
void trampoline_stub_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) {
// Finalize owner destination only for nmethods
if (dest->blob() != nullptr) return;
+ // We either relocate a nmethod residing in CodeCache or just generated code from CodeBuffer
+ assert(src->blob() == nullptr || nativeCall_at(owner())->raw_destination() == owner(), "destination should be empty");
pd_fix_owner_after_move();
}
#endif
diff --git a/src/hotspot/share/compiler/oopMap.cpp b/src/hotspot/share/compiler/oopMap.cpp
index aa010872975..87467d06400 100644
--- a/src/hotspot/share/compiler/oopMap.cpp
+++ b/src/hotspot/share/compiler/oopMap.cpp
@@ -862,6 +862,12 @@ ImmutableOopMapSet* ImmutableOopMapSet::build_from(const OopMapSet* oopmap_set)
return builder.build();
}
+ImmutableOopMapSet* ImmutableOopMapSet::clone() const {
+ address buffer = NEW_C_HEAP_ARRAY(unsigned char, _size, mtCode);
+ memcpy(buffer, (address)this, _size);
+ return (ImmutableOopMapSet*)buffer;
+}
+
void ImmutableOopMapSet::operator delete(void* p) {
FREE_C_HEAP_ARRAY(unsigned char, p);
}
diff --git a/src/hotspot/share/compiler/oopMap.hpp b/src/hotspot/share/compiler/oopMap.hpp
index ef8845ac9aa..f7a8cd8496c 100644
--- a/src/hotspot/share/compiler/oopMap.hpp
+++ b/src/hotspot/share/compiler/oopMap.hpp
@@ -348,6 +348,8 @@ public:
static ImmutableOopMapSet* build_from(const OopMapSet* oopmap_set);
+ ImmutableOopMapSet* clone() const;
+
int find_slot_for_offset(int pc_offset) const;
const ImmutableOopMap* find_map_at_offset(int pc_offset) const;
const ImmutableOopMap* find_map_at_slot(int slot, int pc_offset) const;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp
index 83151313f75..b248fab7958 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp
@@ -103,7 +103,7 @@ public:
}
virtual bool is_safe(nmethod* nm) {
- if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading()) {
+ if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading() || (NMethodState_lock->owned_by_self() && nm->is_not_installed())) {
return true;
}
diff --git a/src/hotspot/share/gc/z/zUnload.cpp b/src/hotspot/share/gc/z/zUnload.cpp
index c8b32385fcd..5c50b3077dd 100644
--- a/src/hotspot/share/gc/z/zUnload.cpp
+++ b/src/hotspot/share/gc/z/zUnload.cpp
@@ -100,7 +100,7 @@ public:
}
virtual bool is_safe(nmethod* nm) {
- if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading()) {
+ if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading() || (NMethodState_lock->owned_by_self() && nm->is_not_installed())) {
return true;
}
diff --git a/src/hotspot/share/jvmci/jvmciRuntime.hpp b/src/hotspot/share/jvmci/jvmciRuntime.hpp
index f4c322e831c..885ff0dbf9b 100644
--- a/src/hotspot/share/jvmci/jvmciRuntime.hpp
+++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp
@@ -137,6 +137,11 @@ public:
// Gets the JVMCI name of the nmethod (which may be null).
const char* name() { return has_name() ? (char*)(((address) this) + sizeof(JVMCINMethodData)) : nullptr; }
+ // Returns true if this nmethod has a mirror
+ bool has_mirror() const {
+ return _nmethod_mirror_index != -1;
+ }
+
// Clears the HotSpotNmethod.address field in the mirror. If nm
// is dead, the HotSpotNmethod.entryPoint field is also cleared.
void invalidate_nmethod_mirror(nmethod* nm, nmethod::InvalidationReason invalidation_reason);
diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp
index f77b648ba95..1ecd105f218 100644
--- a/src/hotspot/share/prims/whitebox.cpp
+++ b/src/hotspot/share/prims/whitebox.cpp
@@ -39,6 +39,7 @@
#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
#include "code/codeCache.hpp"
+#include "code/compiledIC.hpp"
#include "compiler/compilationPolicy.hpp"
#include "compiler/compilerOracle.hpp"
#include "compiler/directivesParser.hpp"
@@ -1548,19 +1549,23 @@ struct CodeBlobStub {
name(os::strdup(blob->name())),
size(blob->size()),
blob_type(static_cast(WhiteBox::get_blob_type(blob))),
- address((jlong) blob) { }
+ address((jlong) blob),
+ code_begin((jlong) blob->code_begin()),
+ is_nmethod((jboolean) blob->is_nmethod()) { }
~CodeBlobStub() { os::free((void*) name); }
const char* const name;
const jint size;
const jint blob_type;
const jlong address;
+ const jlong code_begin;
+ const jboolean is_nmethod;
};
static jobjectArray codeBlob2objectArray(JavaThread* thread, JNIEnv* env, CodeBlobStub* cb) {
ResourceMark rm;
jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string());
CHECK_JNI_EXCEPTION_(env, nullptr);
- jobjectArray result = env->NewObjectArray(4, clazz, nullptr);
+ jobjectArray result = env->NewObjectArray(6, clazz, nullptr);
jstring name = env->NewStringUTF(cb->name);
CHECK_JNI_EXCEPTION_(env, nullptr);
@@ -1578,6 +1583,14 @@ static jobjectArray codeBlob2objectArray(JavaThread* thread, JNIEnv* env, CodeBl
CHECK_JNI_EXCEPTION_(env, nullptr);
env->SetObjectArrayElement(result, 3, obj);
+ obj = longBox(thread, env, cb->code_begin);
+ CHECK_JNI_EXCEPTION_(env, nullptr);
+ env->SetObjectArrayElement(result, 4, obj);
+
+ obj = booleanBox(thread, env, cb->is_nmethod);
+ CHECK_JNI_EXCEPTION_(env, nullptr);
+ env->SetObjectArrayElement(result, 5, obj);
+
return result;
}
@@ -1627,6 +1640,44 @@ WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jbo
return result;
WB_END
+WB_ENTRY(void, WB_RelocateNMethodFromMethod(JNIEnv* env, jobject o, jobject method, jint blob_type))
+ ResourceMark rm(THREAD);
+ jmethodID jmid = reflected_method_to_jmid(thread, env, method);
+ CHECK_JNI_EXCEPTION(env);
+ methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid));
+ nmethod* code = mh->code();
+ if (code != nullptr) {
+ MutexLocker ml_Compile_lock(Compile_lock);
+ CompiledICLocker ic_locker(code);
+ MutexLocker ml_CodeCache_lock(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+ code->relocate(static_cast(blob_type));
+ }
+WB_END
+
+WB_ENTRY(void, WB_RelocateNMethodFromAddr(JNIEnv* env, jobject o, jlong addr, jint blob_type))
+ ResourceMark rm(THREAD);
+ CHECK_JNI_EXCEPTION(env);
+ void* address = (void*) addr;
+
+ if (address == nullptr) {
+ return;
+ }
+
+ MutexLocker ml_Compile_lock(Compile_lock);
+ MutexLocker ml_CompiledIC_lock(CompiledIC_lock, Mutex::_no_safepoint_check_flag);
+ MutexLocker ml_CodeCache_lock(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+
+ // Verify that nmethod address is still valid
+ CodeBlob* blob = CodeCache::find_blob(address);
+ if (blob != nullptr && blob->is_nmethod()) {
+ nmethod* code = blob->as_nmethod();
+ if (code->is_in_use()) {
+ CompiledICLocker ic_locker(code);
+ code->relocate(static_cast(blob_type));
+ }
+ }
+WB_END
+
CodeBlob* WhiteBox::allocate_code_blob(int size, CodeBlobType blob_type) {
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
BufferBlob* blob;
@@ -2916,6 +2967,9 @@ static JNINativeMethod methods[] = {
{CC"getCPUFeatures", CC"()Ljava/lang/String;", (void*)&WB_GetCPUFeatures },
{CC"getNMethod0", CC"(Ljava/lang/reflect/Executable;Z)[Ljava/lang/Object;",
(void*)&WB_GetNMethod },
+ {CC"relocateNMethodFromMethod0", CC"(Ljava/lang/reflect/Executable;I)V",
+ (void*)&WB_RelocateNMethodFromMethod },
+ {CC"relocateNMethodFromAddr", CC"(JI)V", (void*)&WB_RelocateNMethodFromAddr },
{CC"allocateCodeBlob", CC"(II)J", (void*)&WB_AllocateCodeBlob },
{CC"freeCodeBlob", CC"(J)V", (void*)&WB_FreeCodeBlob },
{CC"getCodeHeapEntries", CC"(I)[Ljava/lang/Object;",(void*)&WB_GetCodeHeapEntries },
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index dac01d018bf..513edaf6588 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -1565,6 +1565,9 @@ const int ObjectAlignmentInBytes = 8;
"Start aggressive sweeping if less than X[%] of the total code cache is free.")\
range(0, 100) \
\
+ product(bool, NMethodRelocation, false, EXPERIMENTAL, \
+ "Enables use of experimental function nmethod::relocate()") \
+ \
/* interpreter debugging */ \
develop(intx, BinarySwitchThreshold, 5, \
"Minimal number of lookupswitch entries for rewriting to binary " \
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java
index c8ba2e8b5af..939b47fdd2a 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java
@@ -37,6 +37,7 @@ import sun.jvm.hotspot.utilities.Observer;
public class NMethod extends CodeBlob {
private static long pcDescSize;
+ private static long immutableDataReferencesCounterSize;
private static AddressField methodField;
/** != InvocationEntryBci if this nmethod is an on-stack replacement method */
private static CIntegerField entryBCIField;
@@ -78,24 +79,25 @@ public class NMethod extends CodeBlob {
private static void initialize(TypeDataBase db) {
Type type = db.lookupType("nmethod");
- methodField = type.getAddressField("_method");
- entryBCIField = type.getCIntegerField("_entry_bci");
- osrLinkField = type.getAddressField("_osr_link");
- immutableDataField = type.getAddressField("_immutable_data");
- immutableDataSizeField = type.getCIntegerField("_immutable_data_size");
- exceptionOffsetField = type.getCIntegerField("_exception_offset");
- deoptHandlerOffsetField = type.getCIntegerField("_deopt_handler_offset");
- origPCOffsetField = type.getCIntegerField("_orig_pc_offset");
- stubOffsetField = type.getCIntegerField("_stub_offset");
- scopesPCsOffsetField = type.getCIntegerField("_scopes_pcs_offset");
- scopesDataOffsetField = type.getCIntegerField("_scopes_data_offset");
- handlerTableOffsetField = new CIntField(type.getCIntegerField("_handler_table_offset"), 0);
- nulChkTableOffsetField = new CIntField(type.getCIntegerField("_nul_chk_table_offset"), 0);
- entryOffsetField = new CIntField(type.getCIntegerField("_entry_offset"), 0);
- verifiedEntryOffsetField = new CIntField(type.getCIntegerField("_verified_entry_offset"), 0);
- osrEntryPointField = type.getAddressField("_osr_entry_point");
- compLevelField = new CIntField(type.getCIntegerField("_comp_level"), 0);
- pcDescSize = db.lookupType("PcDesc").getSize();
+ methodField = type.getAddressField("_method");
+ entryBCIField = type.getCIntegerField("_entry_bci");
+ osrLinkField = type.getAddressField("_osr_link");
+ immutableDataField = type.getAddressField("_immutable_data");
+ immutableDataSizeField = type.getCIntegerField("_immutable_data_size");
+ exceptionOffsetField = type.getCIntegerField("_exception_offset");
+ deoptHandlerOffsetField = type.getCIntegerField("_deopt_handler_offset");
+ origPCOffsetField = type.getCIntegerField("_orig_pc_offset");
+ stubOffsetField = type.getCIntegerField("_stub_offset");
+ scopesPCsOffsetField = type.getCIntegerField("_scopes_pcs_offset");
+ scopesDataOffsetField = type.getCIntegerField("_scopes_data_offset");
+ handlerTableOffsetField = new CIntField(type.getCIntegerField("_handler_table_offset"), 0);
+ nulChkTableOffsetField = new CIntField(type.getCIntegerField("_nul_chk_table_offset"), 0);
+ entryOffsetField = new CIntField(type.getCIntegerField("_entry_offset"), 0);
+ verifiedEntryOffsetField = new CIntField(type.getCIntegerField("_verified_entry_offset"), 0);
+ osrEntryPointField = type.getAddressField("_osr_entry_point");
+ compLevelField = new CIntField(type.getCIntegerField("_comp_level"), 0);
+ pcDescSize = db.lookupType("PcDesc").getSize();
+ immutableDataReferencesCounterSize = VM.getVM().getIntSize();
}
public NMethod(Address addr) {
@@ -139,7 +141,7 @@ public class NMethod extends CodeBlob {
public Address scopesDataBegin() { return immutableDataBegin().addOffsetTo(getScopesDataOffset()); }
public Address scopesDataEnd() { return immutableDataBegin().addOffsetTo(getScopesPCsOffset()); }
public Address scopesPCsBegin() { return immutableDataBegin().addOffsetTo(getScopesPCsOffset()); }
- public Address scopesPCsEnd() { return immutableDataEnd(); }
+ public Address scopesPCsEnd() { return immutableDataEnd().addOffsetTo(-immutableDataReferencesCounterSize); }
public Address metadataBegin() { return mutableDataBegin().addOffsetTo(getRelocationSize()); }
public Address metadataEnd() { return mutableDataEnd(); }
@@ -169,7 +171,8 @@ public class NMethod extends CodeBlob {
scopesPCsSize() +
dependenciesSize() +
handlerTableSize() +
- nulChkTableSize();
+ nulChkTableSize() +
+ (int) immutableDataReferencesCounterSize;
}
public boolean constantsContains (Address addr) { return constantsBegin() .lessThanOrEqual(addr) && constantsEnd() .greaterThan(addr); }
diff --git a/test/hotspot/jtreg/compiler/whitebox/CompilerWhiteBoxTest.java b/test/hotspot/jtreg/compiler/whitebox/CompilerWhiteBoxTest.java
index f87292be019..eb0f70af5c4 100644
--- a/test/hotspot/jtreg/compiler/whitebox/CompilerWhiteBoxTest.java
+++ b/test/hotspot/jtreg/compiler/whitebox/CompilerWhiteBoxTest.java
@@ -221,15 +221,28 @@ public abstract class CompilerWhiteBoxTest {
* compilation level.
*/
protected final void checkNotCompiled(boolean isOsr) {
- if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
- throw new RuntimeException(method + " must not be in queue");
+ checkNotCompiled(method, isOsr);
+ }
+
+ /**
+ * Checks, that the specified executable is not (OSR-)compiled.
+ *
+ * @param executable The method or constructor to check.
+ * @param isOsr Check for OSR compilation if true
+ * @throws RuntimeException if {@linkplain #method} is in compiler queue or
+ * is compiled, or if {@linkplain #method} has zero
+ * compilation level.
+ */
+ protected static final void checkNotCompiled(Executable executable, boolean isOsr) {
+ if (WHITE_BOX.isMethodQueuedForCompilation(executable)) {
+ throw new RuntimeException(executable + " must not be in queue");
}
- if (WHITE_BOX.isMethodCompiled(method, isOsr)) {
- throw new RuntimeException(method + " must not be " +
+ if (WHITE_BOX.isMethodCompiled(executable, isOsr)) {
+ throw new RuntimeException(executable + " must not be " +
(isOsr ? "osr_" : "") + "compiled");
}
- if (WHITE_BOX.getMethodCompilationLevel(method, isOsr) != 0) {
- throw new RuntimeException(method + (isOsr ? " osr_" : " ") +
+ if (WHITE_BOX.getMethodCompilationLevel(executable, isOsr) != 0) {
+ throw new RuntimeException(executable + (isOsr ? " osr_" : " ") +
"comp_level must be == 0");
}
}
@@ -242,21 +255,34 @@ public abstract class CompilerWhiteBoxTest {
* has nonzero compilation level
*/
protected final void checkCompiled() {
+ checkCompiled(method, testCase.isOsr());
+ }
+
+ /**
+ * Checks, that the specified executable is compiled.
+ *
+ * @param executable The method or constructor to check.
+ * @param isOsr Check for OSR compilation if true
+ * @throws RuntimeException if {@linkplain #method} isn't in compiler queue
+ * and isn't compiled, or if {@linkplain #method}
+ * has nonzero compilation level
+ */
+ protected static final void checkCompiled(Executable executable, boolean isOsr) {
final long start = System.currentTimeMillis();
- waitBackgroundCompilation();
- if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
+ waitBackgroundCompilation(executable);
+ if (WHITE_BOX.isMethodQueuedForCompilation(executable)) {
System.err.printf("Warning: %s is still in queue after %dms%n",
- method, System.currentTimeMillis() - start);
+ executable, System.currentTimeMillis() - start);
return;
}
- if (!WHITE_BOX.isMethodCompiled(method, testCase.isOsr())) {
- throw new RuntimeException(method + " must be "
- + (testCase.isOsr() ? "osr_" : "") + "compiled");
+ if (!WHITE_BOX.isMethodCompiled(executable, isOsr)) {
+ throw new RuntimeException(executable + " must be "
+ + (isOsr ? "osr_" : "") + "compiled");
}
- if (WHITE_BOX.getMethodCompilationLevel(method, testCase.isOsr())
+ if (WHITE_BOX.getMethodCompilationLevel(executable, isOsr)
== 0) {
- throw new RuntimeException(method
- + (testCase.isOsr() ? " osr_" : " ")
+ throw new RuntimeException(executable
+ + (isOsr ? " osr_" : " ")
+ "comp_level must be != 0");
}
}
diff --git a/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java
new file mode 100644
index 00000000000..25314051fdd
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright Amazon.com Inc. or its 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.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * @test id=Serial
+ * @bug 8316694
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ * @requires vm.opt.DeoptimizeALot != true
+ * @requires vm.gc.Serial
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache -XX:+UseSerialGC
+ * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod
+ */
+
+/*
+ * @test id=Parallel
+ * @bug 8316694
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ * @requires vm.opt.DeoptimizeALot != true
+ * @requires vm.gc.Parallel
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache -XX:+UseParallelGC
+ * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod
+ */
+
+/*
+ * @test id=G1
+ * @bug 8316694
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ * @requires vm.opt.DeoptimizeALot != true
+ * @requires vm.gc.G1
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache -XX:+UseG1GC
+ * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod
+ */
+
+/*
+ * @test id=Shenandoah
+ * @bug 8316694
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ * @requires vm.opt.DeoptimizeALot != true
+ * @requires vm.gc.Shenandoah
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache -XX:+UseShenandoahGC
+ * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod
+ */
+
+/*
+ * @test id=ZGC
+ * @bug 8316694
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ * @requires vm.opt.DeoptimizeALot != true
+ * @requires vm.gc.Z
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache -XX:+UseZGC
+ * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod
+ */
+
+package compiler.whitebox;
+
+import compiler.whitebox.CompilerWhiteBoxTest;
+import java.lang.reflect.Method;
+import jdk.test.whitebox.WhiteBox;
+import jdk.test.whitebox.code.BlobType;
+import jdk.test.whitebox.code.NMethod;
+
+public class DeoptimizeRelocatedNMethod {
+
+ private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+ public static double FUNCTION_RESULT = 0;
+
+ public static void main(String [] args) throws Exception {
+ // Get method that will be relocated
+ Method method = DeoptimizeRelocatedNMethod.class.getMethod("function");
+ WHITE_BOX.testSetDontInlineMethod(method, true);
+
+ // Verify not initially compiled
+ CompilerWhiteBoxTest.checkNotCompiled(method, false);
+
+ // Call function enough to compile
+ callFunction();
+
+ // Verify now compiled
+ CompilerWhiteBoxTest.checkCompiled(method, false);
+
+ // Get newly created nmethod
+ NMethod origNmethod = NMethod.get(method, false);
+
+ // Relocate nmethod and mark old for cleanup
+ WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodProfiled.id);
+
+ // Trigger GC to clean up old nmethod
+ WHITE_BOX.fullGC();
+
+ // Verify function still compiled after old was cleaned up
+ CompilerWhiteBoxTest.checkCompiled(method, false);
+
+ // Get new nmethod and verify it's actually new
+ NMethod newNmethod = NMethod.get(method, false);
+ if (origNmethod.entry_point == newNmethod.entry_point) {
+ throw new RuntimeException("Did not create new nmethod");
+ }
+
+ // Call to verify everything still works
+ function();
+
+ // Deoptimized method
+ WHITE_BOX.deoptimizeMethod(method);
+
+ CompilerWhiteBoxTest.checkNotCompiled(method, false);
+
+ // Call to verify everything still works
+ function();
+ }
+
+ // Call function multiple times to trigger compilation
+ private static void callFunction() {
+ for (int i = 0; i < CompilerWhiteBoxTest.THRESHOLD; i++) {
+ function();
+ }
+ }
+
+ public static void function() {
+ FUNCTION_RESULT = Math.random();
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/whitebox/RelocateNMethod.java b/test/hotspot/jtreg/compiler/whitebox/RelocateNMethod.java
new file mode 100644
index 00000000000..c18a8afa400
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/whitebox/RelocateNMethod.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright Amazon.com Inc. or its 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.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * @test id=Serial
+ * @bug 8316694
+ * @summary test that nmethod::relocate() correctly creates a new nmethod
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @requires vm.opt.DeoptimizeALot != true
+ * @requires vm.gc.Serial
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache
+ * -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod
+ */
+
+/*
+ * @test id=Parallel
+ * @bug 8316694
+ * @summary test that nmethod::relocate() correctly creates a new nmethod
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @requires vm.opt.DeoptimizeALot != true
+ * @requires vm.gc.Parallel
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache
+ * -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod
+ */
+
+/*
+ * @test id=G1
+ * @bug 8316694
+ * @summary test that nmethod::relocate() correctly creates a new nmethod
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @requires vm.opt.DeoptimizeALot != true
+ * @requires vm.gc.G1
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache
+ * -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod
+ */
+
+/*
+ * @test id=Shenandoah
+ * @bug 8316694
+ * @summary test that nmethod::relocate() correctly creates a new nmethod
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @requires vm.opt.DeoptimizeALot != true
+ * @requires vm.gc.Shenandoah
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache
+ * -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod
+ */
+
+/*
+ * @test id=ZGC
+ * @bug 8316694
+ * @summary test that nmethod::relocate() correctly creates a new nmethod
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @requires vm.opt.DeoptimizeALot != true
+ * @requires vm.gc.Z
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache
+ * -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod
+ */
+
+package compiler.whitebox;
+
+import java.lang.reflect.Method;
+import jdk.test.whitebox.code.BlobType;
+import jdk.test.whitebox.code.NMethod;
+import jdk.test.whitebox.WhiteBox;
+
+import compiler.whitebox.CompilerWhiteBoxTest;
+
+public class RelocateNMethod extends CompilerWhiteBoxTest {
+
+ public static void main(String[] args) throws Exception {
+ CompilerWhiteBoxTest.main(RelocateNMethod::new, new String[] {"CONSTRUCTOR_TEST", "METHOD_TEST", "STATIC_TEST"});
+ }
+
+ private RelocateNMethod(TestCase testCase) {
+ super(testCase);
+ // to prevent inlining of #method
+ WHITE_BOX.testSetDontInlineMethod(method, true);
+ }
+
+ @Override
+ protected void test() throws Exception {
+ checkNotCompiled();
+
+ compile();
+
+ checkCompiled();
+ NMethod origNmethod = NMethod.get(method, false);
+
+ WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodProfiled.id);
+
+ WHITE_BOX.fullGC();
+
+ checkCompiled();
+
+ NMethod newNmethod = NMethod.get(method, false);
+ if (origNmethod.entry_point == newNmethod.entry_point) {
+ throw new RuntimeException("Did not create new nmethod");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/whitebox/RelocateNMethodMultiplePaths.java b/test/hotspot/jtreg/compiler/whitebox/RelocateNMethodMultiplePaths.java
new file mode 100644
index 00000000000..49be3eff8c2
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/whitebox/RelocateNMethodMultiplePaths.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright Amazon.com Inc. or its 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.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * @test id=SerialC1
+ * @bug 8316694
+ * @requires vm.debug == true
+ * @requires vm.gc.Serial
+ * @summary test that relocated nmethod is correctly deoptimized
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1
+ * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
+ * compiler.whitebox.RelocateNMethodMultiplePaths
+ */
+
+/*
+ * @test id=SerialC2
+ * @bug 8316694
+ * @requires vm.debug == true
+ * @requires vm.gc.Serial
+ * @summary test that relocated nmethod is correctly deoptimized
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation
+ * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
+ * compiler.whitebox.RelocateNMethodMultiplePaths
+ */
+
+/*
+ * @test id=ParallelC1
+ * @bug 8316694
+ * @requires vm.debug == true
+ * @requires vm.gc.Parallel
+ * @summary test that relocated nmethod is correctly deoptimized
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1
+ * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
+ * compiler.whitebox.RelocateNMethodMultiplePaths
+ */
+
+/*
+ * @test id=ParallelC2
+ * @bug 8316694
+ * @requires vm.debug == true
+ * @requires vm.gc.Parallel
+ * @summary test that relocated nmethod is correctly deoptimized
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation
+ * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
+ * compiler.whitebox.RelocateNMethodMultiplePaths
+ */
+
+/*
+ * @test id=G1C1
+ * @bug 8316694
+ * @requires vm.debug == true
+ * @requires vm.gc.G1
+ * @summary test that relocated nmethod is correctly deoptimized
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1
+ * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
+ * compiler.whitebox.RelocateNMethodMultiplePaths
+ */
+
+/*
+ * @test id=G1C2
+ * @bug 8316694
+ * @requires vm.debug == true
+ * @requires vm.gc.G1
+ * @summary test that relocated nmethod is correctly deoptimized
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation
+ * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
+ * compiler.whitebox.RelocateNMethodMultiplePaths
+ */
+
+/*
+ * @test id=ShenandoahC1
+ * @bug 8316694
+ * @requires vm.debug == true
+ * @requires vm.gc.Shenandoah
+ * @summary test that relocated nmethod is correctly deoptimized
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1
+ * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
+ * compiler.whitebox.RelocateNMethodMultiplePaths
+ */
+
+/*
+ * @test id=ShenandoahC2
+ * @bug 8316694
+ * @requires vm.debug == true
+ * @requires vm.gc.Shenandoah
+ * @summary test that relocated nmethod is correctly deoptimized
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation
+ * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
+ * compiler.whitebox.RelocateNMethodMultiplePaths
+ */
+
+/*
+ * @test id=ZGCC1
+ * @bug 8316694
+ * @requires vm.debug == true
+ * @requires vm.gc.Z
+ * @summary test that relocated nmethod is correctly deoptimized
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1
+ * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
+ * compiler.whitebox.RelocateNMethodMultiplePaths
+ */
+
+/*
+ * @test id=ZGCC2
+ * @bug 8316694
+ * @requires vm.debug == true
+ * @requires vm.gc.Z
+ * @summary test that relocated nmethod is correctly deoptimized
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc java.management
+ *
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation
+ * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
+ * compiler.whitebox.RelocateNMethodMultiplePaths
+ */
+
+package compiler.whitebox;
+
+import compiler.whitebox.CompilerWhiteBoxTest;
+import java.lang.reflect.Method;
+import jdk.test.whitebox.WhiteBox;
+import jdk.test.whitebox.code.BlobType;
+import jdk.test.whitebox.code.NMethod;
+
+public class RelocateNMethodMultiplePaths {
+
+ private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+ private static final int PATH_ONE_RESULT = 1;
+ private static final int PATH_TWO_RESULT = 2;
+
+ public static void main(String [] args) throws Exception {
+ // Get method that will be relocated
+ Method method = RelocateNMethodMultiplePaths.class.getMethod("function", boolean.class);
+ WHITE_BOX.testSetDontInlineMethod(method, true);
+
+ // Verify not initially compiled
+ CompilerWhiteBoxTest.checkNotCompiled(method, false);
+
+ // Call function enough to compile
+ callFunction(true);
+
+ // Verify now compiled
+ CompilerWhiteBoxTest.checkCompiled(method, false);
+
+ // Get newly created nmethod
+ NMethod origNmethod = NMethod.get(method, false);
+
+ // Relocate nmethod and mark old for cleanup
+ WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodNonProfiled.id);
+
+ // Trigger GC to clean up old nmethod
+ WHITE_BOX.fullGC();
+
+ // Verify function still compiled after old was cleaned up
+ CompilerWhiteBoxTest.checkCompiled(method, false);
+
+ // Get new nmethod and verify it's actually new
+ NMethod newNmethod = NMethod.get(method, false);
+ if (origNmethod.entry_point == newNmethod.entry_point) {
+ throw new RuntimeException("Did not create new nmethod");
+ }
+
+ // Verify function still produces correct result
+ if (function(true) != PATH_ONE_RESULT) {
+ throw new RuntimeException("Relocated function produced incorrect result in path one");
+ }
+
+ // Call function again with different path and verify result
+ if (function(false) != PATH_TWO_RESULT) {
+ throw new RuntimeException("Relocated function produced incorrect result in path two");
+ }
+
+ // Verify function can be correctly deoptimized
+ WHITE_BOX.deoptimizeMethod(method);
+ CompilerWhiteBoxTest.checkNotCompiled(method, false);
+ }
+
+ // Call function multiple times to trigger compilation
+ private static void callFunction(boolean pathOne) {
+ for (int i = 0; i < CompilerWhiteBoxTest.THRESHOLD; i++) {
+ function(pathOne);
+ }
+ }
+
+ public static int function(boolean pathOne) {
+ if (pathOne) {
+ return PATH_ONE_RESULT;
+ } else {
+ return PATH_TWO_RESULT;
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/whitebox/StressNMethodRelocation.java b/test/hotspot/jtreg/compiler/whitebox/StressNMethodRelocation.java
new file mode 100644
index 00000000000..3b397f48306
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/whitebox/StressNMethodRelocation.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright Amazon.com Inc. or its 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.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * @test StressNMethodRelocation
+ * @summary Call and relocate methods concurrently
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc
+ * java.management
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+SegmentedCodeCache -XX:+UnlockExperimentalVMOptions
+ * -XX:+NMethodRelocation compiler.whitebox.StressNMethodRelocation
+ */
+
+package compiler.whitebox;
+
+import jdk.test.whitebox.WhiteBox;
+import jdk.test.whitebox.code.BlobType;
+import jdk.test.whitebox.code.CodeBlob;
+import jdk.test.whitebox.code.NMethod;
+
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Random;
+
+public class StressNMethodRelocation {
+ private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+ private static final int C2_LEVEL = 4;
+ private static final int ACTIVE_METHODS = 1024;
+
+ private static TestMethod[] methods;
+ private static byte[] num1;
+ private static byte[] num2;
+
+ private static long DURATION = 60_000;
+
+ public static void main(String[] args) throws Exception {
+ // Initialize defaults
+ initNums();
+
+ // Generate compiled code
+ methods = new TestMethod[ACTIVE_METHODS];
+ generateCode(methods);
+
+ // Create thread that runs compiled methods
+ RunMethods runMethods = new RunMethods();
+ Thread runMethodsThread = new Thread(runMethods);
+
+ // Create thread that relocates compiled methods
+ RelocateNMethods relocate = new RelocateNMethods();
+ Thread relocateThread = new Thread(relocate);
+
+ // Start theads
+ runMethodsThread.start();
+ relocateThread.start();
+
+ // Wait for threads to finish
+ runMethodsThread.join();
+ relocateThread.join();
+ }
+
+ private static byte[] genNum(Random random, int digitCount) {
+ byte[] num = new byte[digitCount];
+ int d;
+ do {
+ d = random.nextInt(10);
+ } while (d == 0);
+
+ num[0] = (byte)d;
+ for (int i = 1; i < digitCount; ++i) {
+ num[i] = (byte)random.nextInt(10);
+ }
+ return num;
+ }
+
+ private static void initNums() {
+ final long seed = 8374592837465123L;
+ Random random = new Random(seed);
+
+ final int digitCount = 40;
+ num1 = genNum(random, digitCount);
+ num2 = genNum(random, digitCount);
+ }
+
+ private static void generateCode(TestMethod[] m) throws Exception {
+ byte[] result = new byte[num1.length + 1];
+
+ for (int i = 0; i < ACTIVE_METHODS; ++i) {
+ m[i] = new TestMethod();
+ m[i].profile(num1, num2, result);
+ m[i].compileWithC2();
+ }
+ }
+
+ private static final class TestMethod {
+ private static final String CLASS_NAME = "A";
+ private static final String METHOD_TO_COMPILE = "sum";
+ private static final String JAVA_CODE = """
+ public class A {
+
+ public static void sum(byte[] n1, byte[] n2, byte[] out) {
+ final int digitCount = n1.length;
+ int carry = 0;
+ for (int i = digitCount - 1; i >= 0; --i) {
+ int sum = n1[i] + n2[i] + carry;
+ out[i] = (byte)(sum % 10);
+ carry = sum / 10;
+ }
+ if (carry != 0) {
+ for (int i = digitCount; i > 0; --i) {
+ out[i] = out[i - 1];
+ }
+ out[0] = (byte)carry;
+ }
+ }
+ }""";
+
+ private static final byte[] BYTE_CODE;
+
+ static {
+ BYTE_CODE = InMemoryJavaCompiler.compile(CLASS_NAME, JAVA_CODE);
+ }
+
+ private final Method method;
+
+ private static ClassLoader createClassLoaderFor() {
+ return new ClassLoader() {
+ @Override
+ public Class> loadClass(String name) throws ClassNotFoundException {
+ if (!name.equals(CLASS_NAME)) {
+ return super.loadClass(name);
+ }
+
+ return defineClass(name, BYTE_CODE, 0, BYTE_CODE.length);
+ }
+ };
+ }
+
+ public TestMethod() throws Exception {
+ var cl = createClassLoaderFor().loadClass(CLASS_NAME);
+ method = cl.getMethod(METHOD_TO_COMPILE, byte[].class, byte[].class, byte[].class);
+ WHITE_BOX.testSetDontInlineMethod(method, true);
+ }
+
+ public void profile(byte[] num1, byte[] num2, byte[] result) throws Exception {
+ method.invoke(null, num1, num2, result);
+ WHITE_BOX.markMethodProfiled(method);
+ }
+
+ public void invoke(byte[] num1, byte[] num2, byte[] result) throws Exception {
+ method.invoke(null, num1, num2, result);
+ }
+
+ public void compileWithC2() throws Exception {
+ WHITE_BOX.enqueueMethodForCompilation(method, C2_LEVEL);
+ while (WHITE_BOX.isMethodQueuedForCompilation(method)) {
+ Thread.onSpinWait();
+ }
+ if (WHITE_BOX.getMethodCompilationLevel(method) != C2_LEVEL) {
+ throw new IllegalStateException("Method " + method + " is not compiled by C2.");
+ }
+ }
+ }
+
+ private static final class RelocateNMethods implements Runnable {
+ public RelocateNMethods() {}
+
+ // Move nmethod back and forth between NonProfiled and Profiled code heaps
+ public void run() {
+ long startTime = System.currentTimeMillis();
+ while (System.currentTimeMillis() - startTime < DURATION) {
+ // Relocate NonProfiled to Profiled
+ CodeBlob[] nonProfiledBlobs = CodeBlob.getCodeBlobs(BlobType.MethodNonProfiled);
+ for (CodeBlob blob : nonProfiledBlobs) {
+ if (blob.isNMethod) {
+ WHITE_BOX.relocateNMethodFromAddr(blob.address, BlobType.MethodProfiled.id);
+ }
+ }
+
+ // Relocate Profiled to NonProfiled
+ CodeBlob[] profiledBlobs = CodeBlob.getCodeBlobs(BlobType.MethodProfiled);
+ for (CodeBlob blob : nonProfiledBlobs) {
+ if (blob.isNMethod) {
+ WHITE_BOX.relocateNMethodFromAddr(blob.address, BlobType.MethodNonProfiled.id);
+ }
+ }
+ }
+ }
+ }
+
+ private static final class RunMethods implements Runnable {
+ public RunMethods() {}
+
+ public void run() {
+ try {
+ long startTime = System.currentTimeMillis();
+ while (System.currentTimeMillis() - startTime < DURATION) {
+ callMethods();
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ private void callMethods() throws Exception {
+ for (var m : methods) {
+ byte[] result = new byte[num1.length + 1];
+ m.invoke(num1, num2, result);
+ }
+ }
+ }
+
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java
new file mode 100644
index 00000000000..b12e2c3455c
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright Amazon.com Inc. or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ *
+ * @bug 8316694
+ * @summary Verify that nmethod relocation posts the correct JVMTI events
+ * @requires vm.jvmti
+ * @library /test/lib /test/hotspot/jtreg
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm/native NMethodRelocationTest
+ */
+
+import static compiler.whitebox.CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.whitebox.WhiteBox;
+import jdk.test.whitebox.code.BlobType;
+import jdk.test.whitebox.code.NMethod;
+
+
+public class NMethodRelocationTest {
+ public static void main(String[] args) throws Exception {
+ ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
+ "-agentlib:NMethodRelocationTest",
+ "--enable-native-access=ALL-UNNAMED",
+ "-Xbootclasspath/a:.",
+ "-XX:+UseSerialGC",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "-XX:+SegmentedCodeCache",
+ "-XX:-TieredCompilation",
+ "-XX:+UnlockExperimentalVMOptions",
+ "-XX:+NMethodRelocation",
+ "DoWork");
+
+ OutputAnalyzer oa = new OutputAnalyzer(pb.start());
+ String output = oa.getOutput();
+ if (oa.getExitValue() != 0) {
+ System.err.println(oa.getOutput());
+ throw new RuntimeException("Non-zero exit code returned from the test");
+ }
+ Asserts.assertTrue(oa.getExitValue() == 0);
+
+ Pattern pattern = Pattern.compile("(?m)^Relocated nmethod from (0x[0-9a-f]{16}) to (0x[0-9a-f]{16})$");
+ Matcher matcher = pattern.matcher(output);
+
+ if (matcher.find()) {
+ String fromAddr = matcher.group(1);
+ String toAddr = matcher.group(2);
+
+ // Confirm events sent for both original and relocated nmethod
+ oa.shouldContain(": name: compiledMethod, code: " + fromAddr);
+ oa.shouldContain(": name: compiledMethod, code: " + toAddr);
+ oa.shouldContain(": name: compiledMethod, code: " + fromAddr);
+ oa.shouldContain(": name: compiledMethod, code: " + toAddr);
+ } else {
+ System.err.println(oa.getOutput());
+ throw new RuntimeException("Unable to find relocation information");
+ }
+ }
+}
+
+class DoWork {
+
+ protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+ /** Load native library if required. */
+ static {
+ try {
+ System.loadLibrary("NMethodRelocationTest");
+ } catch (UnsatisfiedLinkError ule) {
+ System.err.println("Could not load NMethodRelocationTest library");
+ System.err.println("java.library.path: "
+ + System.getProperty("java.library.path"));
+ throw ule;
+ }
+ }
+
+ /**
+ * Returns value of VM option.
+ *
+ * @param name option's name
+ * @return value of option or {@code null}, if option doesn't exist
+ * @throws NullPointerException if name is null
+ */
+ protected static String getVMOption(String name) {
+ Objects.requireNonNull(name);
+ return Objects.toString(WHITE_BOX.getVMFlag(name), null);
+ }
+
+ /**
+ * Returns value of VM option or default value.
+ *
+ * @param name option's name
+ * @param defaultValue default value
+ * @return value of option or {@code defaultValue}, if option doesn't exist
+ * @throws NullPointerException if name is null
+ * @see #getVMOption(String)
+ */
+ protected static String getVMOption(String name, String defaultValue) {
+ String result = getVMOption(name);
+ return result == null ? defaultValue : result;
+ }
+
+ public static void main(String argv[]) throws Exception {
+ run();
+ }
+
+ public static void run() throws Exception {
+ Executable method = DoWork.class.getDeclaredMethod("compiledMethod");
+ WHITE_BOX.testSetDontInlineMethod(method, true);
+
+ WHITE_BOX.enqueueMethodForCompilation(method, COMP_LEVEL_FULL_OPTIMIZATION);
+ while (WHITE_BOX.isMethodQueuedForCompilation(method)) {
+ Thread.onSpinWait();
+ }
+
+ NMethod originalNMethod = NMethod.get(method, false);
+ if (originalNMethod == null) {
+ throw new AssertionError("Could not find original nmethod");
+ }
+
+ WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodNonProfiled.id);
+
+ NMethod relocatedNMethod = NMethod.get(method, false);
+ if (relocatedNMethod == null) {
+ throw new AssertionError("Could not find relocated nmethod");
+ }
+
+ if (originalNMethod.address == relocatedNMethod.address) {
+ throw new AssertionError("Relocated nmethod same as original");
+ }
+
+ WHITE_BOX.deoptimizeAll();
+
+ WHITE_BOX.fullGC();
+ WHITE_BOX.fullGC();
+
+ WHITE_BOX.lockCompilation();
+
+ System.out.printf("Relocated nmethod from 0x%016x to 0x%016x%n", originalNMethod.code_begin, relocatedNMethod.code_begin);
+ System.out.flush();
+ }
+
+ public static long compiledMethod() {
+ return 0;
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp
new file mode 100644
index 00000000000..41ba6b10608
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright Amazon.com Inc. or its 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.
+ *
+ * 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.
+ */
+
+#include
+#include
+#include
+#include
+
+/**
+ * Callback for COMPILED_METHOD_LOAD event.
+ */
+JNIEXPORT void JNICALL
+callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method,
+ jint code_size, const void* code_addr,
+ jint map_length, const jvmtiAddrLocationMap* map,
+ const void* compile_info) {
+ char* name = nullptr;
+ char* sig = nullptr;
+
+ if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) {
+ printf(" [Could not retrieve method name]\n");
+ fflush(stdout);
+ return;
+ }
+
+ printf(": name: %s, code: 0x%016" PRIxPTR "\n",
+ name, (uintptr_t)code_addr);
+ fflush(stdout);
+}
+
+/**
+ * Callback for COMPILED_METHOD_UNLOAD event.
+ */
+JNIEXPORT void JNICALL
+callbackCompiledMethodUnload(jvmtiEnv* jvmti, jmethodID method,
+ const void* code_addr) {
+ char* name = nullptr;
+ char* sig = nullptr;
+
+ if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) {
+ printf(" [Could not retrieve method name]\n");
+ fflush(stdout);
+ return;
+ }
+ printf(": name: %s, code: 0x%016" PRIxPTR "\n",
+ name, (uintptr_t)code_addr);
+ fflush(stdout);
+}
+
+JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
+ jvmtiEnv* jvmti = nullptr;
+ jvmtiError error;
+
+ if (jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_0) != JNI_OK) {
+ printf("Unable to access JVMTI!\n");
+ return JNI_ERR;
+ }
+
+ // Add required capabilities
+ jvmtiCapabilities caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.can_generate_compiled_method_load_events = 1;
+ error = jvmti->AddCapabilities(&caps);
+ if (error != JVMTI_ERROR_NONE) {
+ printf("ERROR: Unable to add capabilities, error=%d\n", error);
+ return JNI_ERR;
+ }
+
+ // Set event callbacks
+ jvmtiEventCallbacks eventCallbacks;
+ memset(&eventCallbacks, 0, sizeof(eventCallbacks));
+ eventCallbacks.CompiledMethodLoad = callbackCompiledMethodLoad;
+ eventCallbacks.CompiledMethodUnload = callbackCompiledMethodUnload;
+ error = jvmti->SetEventCallbacks(&eventCallbacks, sizeof(eventCallbacks));
+ if (error != JVMTI_ERROR_NONE) {
+ printf("ERROR: Unable to set event callbacks, error=%d\n", error);
+ return JNI_ERR;
+ }
+
+ // Enable events
+ error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, nullptr);
+ if (error != JVMTI_ERROR_NONE) {
+ printf("ERROR: Unable to enable COMPILED_METHOD_LOAD event, error=%d\n", error);
+ return JNI_ERR;
+ }
+
+ error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, nullptr);
+ if (error != JVMTI_ERROR_NONE) {
+ printf("ERROR: Unable to enable COMPILED_METHOD_UNLOAD event, error=%d\n", error);
+ return JNI_ERR;
+ }
+
+ return JNI_OK;
+}
diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java
index 5adb7bf5127..669ec48b619 100644
--- a/test/lib/jdk/test/whitebox/WhiteBox.java
+++ b/test/lib/jdk/test/whitebox/WhiteBox.java
@@ -490,6 +490,12 @@ public class WhiteBox {
Objects.requireNonNull(method);
return getNMethod0(method, isOsr);
}
+ private native void relocateNMethodFromMethod0(Executable method, int type);
+ public void relocateNMethodFromMethod(Executable method, int type) {
+ Objects.requireNonNull(method);
+ relocateNMethodFromMethod0(method, type);
+ }
+ public native void relocateNMethodFromAddr(long address, int type);
public native long allocateCodeBlob(int size, int type);
public long allocateCodeBlob(long size, int type) {
int intSize = (int) size;
diff --git a/test/lib/jdk/test/whitebox/code/CodeBlob.java b/test/lib/jdk/test/whitebox/code/CodeBlob.java
index c6c23fdff0c..fd95b5a7e7d 100644
--- a/test/lib/jdk/test/whitebox/code/CodeBlob.java
+++ b/test/lib/jdk/test/whitebox/code/CodeBlob.java
@@ -46,18 +46,22 @@ public class CodeBlob {
return new CodeBlob(obj);
}
protected CodeBlob(Object[] obj) {
- assert obj.length == 4;
+ assert obj.length == 6;
name = (String) obj[0];
size = (Integer) obj[1];
int blob_type_index = (Integer) obj[2];
code_blob_type = BlobType.values()[blob_type_index];
assert code_blob_type.id == (Integer) obj[2];
address = (Long) obj[3];
+ code_begin = (Long) obj[4];
+ isNMethod = (Boolean) obj[5];
}
public final String name;
public final int size;
public final BlobType code_blob_type;
public final long address;
+ public final long code_begin;
+ public final boolean isNMethod;
@Override
public String toString() {
return "CodeBlob{"
@@ -65,6 +69,7 @@ public class CodeBlob {
+ ", size=" + size
+ ", code_blob_type=" + code_blob_type
+ ", address=" + address
+ + ", code_begin=" + code_begin
+ '}';
}
}
From 5d9f94e05e1527745271d0167a418741607619e2 Mon Sep 17 00:00:00 2001
From: Vladimir Kozlov
Date: Sun, 5 Oct 2025 16:20:53 +0000
Subject: [PATCH 023/160] 8369152: Problem list new tests from JDK-8316694
Reviewed-by: jpai, dholmes, serb
---
test/hotspot/jtreg/ProblemList.txt | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
index d061236c957..f02ba70ba87 100644
--- a/test/hotspot/jtreg/ProblemList.txt
+++ b/test/hotspot/jtreg/ProblemList.txt
@@ -79,6 +79,17 @@ compiler/c2/TestVerifyConstraintCasts.java 8355574 generic-all
compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64
+compiler/whitebox/DeoptimizeRelocatedNMethod.java#G1 8369147 generic-all
+compiler/whitebox/DeoptimizeRelocatedNMethod.java#Parallel 8369147 generic-all
+compiler/whitebox/DeoptimizeRelocatedNMethod.java#Serial 8369147 generic-all
+compiler/whitebox/DeoptimizeRelocatedNMethod.java#ZGC 8369147 generic-all
+compiler/whitebox/RelocateNMethod.java#G1 8369147 generic-all
+compiler/whitebox/RelocateNMethod.java#Parallel 8369147 generic-all
+compiler/whitebox/RelocateNMethod.java#Serial 8369147 generic-all
+compiler/whitebox/RelocateNMethod.java#ZGC 8369147 generic-all
+compiler/whitebox/StressNMethodRelocation.java 8369147,8369148,8369149 generic-all
+serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java 8369150,8369151 generic-all
+
#############################################################################
# :hotspot_gc
From ba7bf43c76c94bea85dbbd865794184b7ee0cc86 Mon Sep 17 00:00:00 2001
From: Vladimir Ivanov
Date: Sun, 5 Oct 2025 23:55:53 +0000
Subject: [PATCH 024/160] 8365290: [perf] x86 ArrayFill intrinsic generates
SPLIT_STORE for unaligned arrays
Reviewed-by: sviswanathan, vpaprotski, kvn
---
src/hotspot/cpu/x86/macroAssembler_x86.cpp | 47 ++++++++++++++++++----
1 file changed, 40 insertions(+), 7 deletions(-)
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
index c1319b2ef7f..77ee71c0382 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
@@ -5847,7 +5847,7 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned,
orl(value, rtmp);
}
- cmpptr(count, 2<
Date: Mon, 6 Oct 2025 06:17:48 +0000
Subject: [PATCH 025/160] 8355354: C2 crashed: assert(_callee == nullptr ||
_callee == m) failed: repeated inline attempt with different callee
Reviewed-by: vlivanov, dlong
---
src/hotspot/share/opto/callGenerator.cpp | 4 ++
src/hotspot/share/opto/callGenerator.hpp | 1 +
src/hotspot/share/opto/callnode.cpp | 52 +++++++++++++-----------
src/hotspot/share/opto/compile.cpp | 6 +++
src/hotspot/share/opto/compile.hpp | 3 +-
5 files changed, 41 insertions(+), 25 deletions(-)
diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp
index 03225044f5e..483cb731103 100644
--- a/src/hotspot/share/opto/callGenerator.cpp
+++ b/src/hotspot/share/opto/callGenerator.cpp
@@ -465,6 +465,10 @@ class LateInlineVirtualCallGenerator : public VirtualCallGenerator {
// Convert the CallDynamicJava into an inline
virtual void do_late_inline();
+ virtual ciMethod* callee_method() {
+ return _callee;
+ }
+
virtual void set_callee_method(ciMethod* m) {
assert(_callee == nullptr || _callee == m, "repeated inline attempt with different callee");
_callee = m;
diff --git a/src/hotspot/share/opto/callGenerator.hpp b/src/hotspot/share/opto/callGenerator.hpp
index 82b195e0c76..e24ea5e5356 100644
--- a/src/hotspot/share/opto/callGenerator.hpp
+++ b/src/hotspot/share/opto/callGenerator.hpp
@@ -88,6 +88,7 @@ class CallGenerator : public ArenaObj {
virtual void set_unique_id(jlong id) { fatal("unique id only for late inlines"); };
virtual jlong unique_id() const { fatal("unique id only for late inlines"); return 0; };
+ virtual ciMethod* callee_method() { ShouldNotReachHere(); }
virtual void set_callee_method(ciMethod* callee) { ShouldNotReachHere(); }
// Note: It is possible for a CG to be both inline and virtual.
diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp
index 995208ba24f..ef1ebc5cef9 100644
--- a/src/hotspot/share/opto/callnode.cpp
+++ b/src/hotspot/share/opto/callnode.cpp
@@ -1228,33 +1228,37 @@ Node* CallDynamicJavaNode::Ideal(PhaseGVN* phase, bool can_reshape) {
assert(IncrementalInlineVirtual, "required");
assert(cg->call_node() == this, "mismatch");
- // Recover symbolic info for method resolution.
- ciMethod* caller = jvms()->method();
- ciBytecodeStream iter(caller);
- iter.force_bci(jvms()->bci());
+ if (cg->callee_method() == nullptr) {
+ // Recover symbolic info for method resolution.
+ ciMethod* caller = jvms()->method();
+ ciBytecodeStream iter(caller);
+ iter.force_bci(jvms()->bci());
- bool not_used1;
- ciSignature* not_used2;
- ciMethod* orig_callee = iter.get_method(not_used1, ¬_used2); // callee in the bytecode
- ciKlass* holder = iter.get_declared_method_holder();
- if (orig_callee->is_method_handle_intrinsic()) {
- assert(_override_symbolic_info, "required");
- orig_callee = method();
- holder = method()->holder();
+ bool not_used1;
+ ciSignature* not_used2;
+ ciMethod* orig_callee = iter.get_method(not_used1, ¬_used2); // callee in the bytecode
+ ciKlass* holder = iter.get_declared_method_holder();
+ if (orig_callee->is_method_handle_intrinsic()) {
+ assert(_override_symbolic_info, "required");
+ orig_callee = method();
+ holder = method()->holder();
+ }
+
+ ciInstanceKlass* klass = ciEnv::get_instance_klass_for_declared_method_holder(holder);
+
+ Node* receiver_node = in(TypeFunc::Parms);
+ const TypeOopPtr* receiver_type = phase->type(receiver_node)->isa_oopptr();
+
+ int not_used3;
+ bool call_does_dispatch;
+ ciMethod* callee = phase->C->optimize_virtual_call(caller, klass, holder, orig_callee, receiver_type, true /*is_virtual*/,
+ call_does_dispatch, not_used3); // out-parameters
+ if (!call_does_dispatch) {
+ cg->set_callee_method(callee);
+ }
}
-
- ciInstanceKlass* klass = ciEnv::get_instance_klass_for_declared_method_holder(holder);
-
- Node* receiver_node = in(TypeFunc::Parms);
- const TypeOopPtr* receiver_type = phase->type(receiver_node)->isa_oopptr();
-
- int not_used3;
- bool call_does_dispatch;
- ciMethod* callee = phase->C->optimize_virtual_call(caller, klass, holder, orig_callee, receiver_type, true /*is_virtual*/,
- call_does_dispatch, not_used3); // out-parameters
- if (!call_does_dispatch) {
+ if (cg->callee_method() != nullptr) {
// Register for late inlining.
- cg->set_callee_method(callee);
register_for_late_inline(); // MH late inlining prepends to the list, so do the same
}
} else {
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index 47de5acc2f2..7f63efe9e5e 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -2104,6 +2104,12 @@ bool Compile::inline_incrementally_one() {
bool is_scheduled_for_igvn_before = C->igvn_worklist()->member(cg->call_node());
bool does_dispatch = cg->is_virtual_late_inline() || cg->is_mh_late_inline();
if (inlining_incrementally() || does_dispatch) { // a call can be either inlined or strength-reduced to a direct call
+ if (should_stress_inlining()) {
+ // randomly add repeated inline attempt if stress-inlining
+ cg->call_node()->set_generator(cg);
+ C->igvn_worklist()->push(cg->call_node());
+ continue;
+ }
cg->do_late_inline();
assert(_late_inlines.at(i) == cg, "no insertions before current position allowed");
if (failing()) {
diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp
index 05c5f22dad9..a68da644d82 100644
--- a/src/hotspot/share/opto/compile.hpp
+++ b/src/hotspot/share/opto/compile.hpp
@@ -1094,7 +1094,8 @@ public:
bool inline_incrementally_one();
void inline_incrementally_cleanup(PhaseIterGVN& igvn);
void inline_incrementally(PhaseIterGVN& igvn);
- bool should_delay_inlining() { return AlwaysIncrementalInline || (StressIncrementalInlining && (random() % 2) == 0); }
+ bool should_stress_inlining() { return StressIncrementalInlining && (random() % 2) == 0; }
+ bool should_delay_inlining() { return AlwaysIncrementalInline || should_stress_inlining(); }
void inline_string_calls(bool parse_time);
void inline_boxing_calls(PhaseIterGVN& igvn);
bool optimize_loops(PhaseIterGVN& igvn, LoopOptsMode mode);
From 069c569a710f50bc715f523c6c4c7aa087694af6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Johan=20Sj=C3=B6len?=
Date: Mon, 6 Oct 2025 07:48:45 +0000
Subject: [PATCH 026/160] 8368097: [asan] heap-buffer-overflow reported in
ClassFileParser::skip_over_field_signature
Reviewed-by: dholmes, mbaesken
---
src/hotspot/share/classfile/classFileParser.cpp | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index fddd9df726b..87f2da91288 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -4678,11 +4678,15 @@ const char* ClassFileParser::skip_over_field_signature(const char* signature,
return signature + 1;
case JVM_SIGNATURE_CLASS: {
if (_major_version < JAVA_1_5_VERSION) {
+ signature++;
+ length--;
// Skip over the class name if one is there
- const char* const p = skip_over_field_name(signature + 1, true, --length);
-
+ const char* const p = skip_over_field_name(signature, true, length);
+ assert(p == nullptr || p > signature, "must parse one character at least");
// The next character better be a semicolon
- if (p && (p - signature) > 1 && p[0] == JVM_SIGNATURE_ENDCLASS) {
+ if (p != nullptr && // Parse of field name succeeded.
+ p - signature < static_cast(length) && // There is at least one character left to parse.
+ p[0] == JVM_SIGNATURE_ENDCLASS) {
return p + 1;
}
}
From e6781fd9497723a7baab38d6bfb958ba1b1c24ff Mon Sep 17 00:00:00 2001
From: Fredrik Bredberg
Date: Mon, 6 Oct 2025 08:10:11 +0000
Subject: [PATCH 027/160] 8367601: Remove held_monitor_count
Reviewed-by: mdoerr, pchilanomate, fyang
---
.../cpu/aarch64/globalDefinitions_aarch64.hpp | 2 -
.../cpu/aarch64/macroAssembler_aarch64.cpp | 32 ----------
.../cpu/aarch64/macroAssembler_aarch64.hpp | 3 -
.../cpu/aarch64/sharedRuntime_aarch64.cpp | 47 ---------------
src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp | 4 +-
src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 58 +------------------
.../cpu/riscv/globalDefinitions_riscv.hpp | 4 +-
.../cpu/riscv/macroAssembler_riscv.cpp | 30 ----------
.../cpu/riscv/macroAssembler_riscv.hpp | 5 +-
src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp | 47 ---------------
src/hotspot/cpu/x86/globalDefinitions_x86.hpp | 4 +-
src/hotspot/cpu/x86/macroAssembler_x86.cpp | 8 ---
src/hotspot/cpu/x86/macroAssembler_x86.hpp | 3 -
src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp | 46 ---------------
src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 1 -
src/hotspot/share/runtime/continuation.hpp | 9 ++-
.../share/runtime/continuationEntry.cpp | 1 -
.../share/runtime/continuationEntry.hpp | 7 ---
.../share/runtime/continuationFreezeThaw.cpp | 14 +----
src/hotspot/share/runtime/javaThread.cpp | 43 --------------
src/hotspot/share/runtime/javaThread.hpp | 12 ----
src/hotspot/share/runtime/objectMonitor.cpp | 1 -
src/hotspot/share/runtime/sharedRuntime.cpp | 15 -----
src/hotspot/share/runtime/sharedRuntime.hpp | 3 -
src/hotspot/share/runtime/synchronizer.cpp | 8 +--
.../share/runtime/synchronizer.inline.hpp | 2 -
.../classes/jdk/internal/vm/Continuation.java | 9 +--
27 files changed, 17 insertions(+), 401 deletions(-)
diff --git a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp
index 948ba97aa22..1e788590b64 100644
--- a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp
@@ -35,8 +35,6 @@ const bool CCallingConventionRequiresIntsAsLongs = false;
#define SUPPORTS_NATIVE_CX8
-#define SUPPORT_MONITOR_COUNT
-
// Aarch64 was not originally defined to be multi-copy-atomic, but now
// is. See: "Simplifying ARM Concurrency: Multicopy-atomic Axiomatic
// and Operational Models for ARMv8"
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
index 3999beeec2b..2622bda1d0b 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
@@ -5630,38 +5630,6 @@ void MacroAssembler::tlab_allocate(Register obj,
bs->tlab_allocate(this, obj, var_size_in_bytes, con_size_in_bytes, t1, t2, slow_case);
}
-void MacroAssembler::inc_held_monitor_count(Register tmp) {
- Address dst(rthread, JavaThread::held_monitor_count_offset());
-#ifdef ASSERT
- ldr(tmp, dst);
- increment(tmp);
- str(tmp, dst);
- Label ok;
- tbz(tmp, 63, ok);
- STOP("assert(held monitor count underflow)");
- should_not_reach_here();
- bind(ok);
-#else
- increment(dst);
-#endif
-}
-
-void MacroAssembler::dec_held_monitor_count(Register tmp) {
- Address dst(rthread, JavaThread::held_monitor_count_offset());
-#ifdef ASSERT
- ldr(tmp, dst);
- decrement(tmp);
- str(tmp, dst);
- Label ok;
- tbz(tmp, 63, ok);
- STOP("assert(held monitor count underflow)");
- should_not_reach_here();
- bind(ok);
-#else
- decrement(dst);
-#endif
-}
-
void MacroAssembler::verify_tlab() {
#ifdef ASSERT
if (UseTLAB && VerifyOops) {
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
index 0570fad5b8d..705bd19093c 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
@@ -983,9 +983,6 @@ public:
void push_cont_fastpath(Register java_thread = rthread);
void pop_cont_fastpath(Register java_thread = rthread);
- void inc_held_monitor_count(Register tmp);
- void dec_held_monitor_count(Register tmp);
-
// Round up to a power of two
void round_to(Register reg, int modulus);
diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
index 70af8dd91d8..39609cbe0ac 100644
--- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
@@ -985,11 +985,8 @@ static void fill_continuation_entry(MacroAssembler* masm) {
__ ldr(rscratch1, Address(rthread, JavaThread::cont_fastpath_offset()));
__ str(rscratch1, Address(sp, ContinuationEntry::parent_cont_fastpath_offset()));
- __ ldr(rscratch1, Address(rthread, JavaThread::held_monitor_count_offset()));
- __ str(rscratch1, Address(sp, ContinuationEntry::parent_held_monitor_count_offset()));
__ str(zr, Address(rthread, JavaThread::cont_fastpath_offset()));
- __ str(zr, Address(rthread, JavaThread::held_monitor_count_offset()));
}
// on entry, sp points to the ContinuationEntry
@@ -1005,50 +1002,6 @@ static void continuation_enter_cleanup(MacroAssembler* masm) {
#endif
__ ldr(rscratch1, Address(sp, ContinuationEntry::parent_cont_fastpath_offset()));
__ str(rscratch1, Address(rthread, JavaThread::cont_fastpath_offset()));
-
- if (CheckJNICalls) {
- // Check if this is a virtual thread continuation
- Label L_skip_vthread_code;
- __ ldrw(rscratch1, Address(sp, ContinuationEntry::flags_offset()));
- __ cbzw(rscratch1, L_skip_vthread_code);
-
- // If the held monitor count is > 0 and this vthread is terminating then
- // it failed to release a JNI monitor. So we issue the same log message
- // that JavaThread::exit does.
- __ ldr(rscratch1, Address(rthread, JavaThread::jni_monitor_count_offset()));
- __ cbz(rscratch1, L_skip_vthread_code);
-
- // Save return value potentially containing the exception oop in callee-saved R19.
- __ mov(r19, r0);
- __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::log_jni_monitor_still_held));
- // Restore potential return value.
- __ mov(r0, r19);
-
- // For vthreads we have to explicitly zero the JNI monitor count of the carrier
- // on termination. The held count is implicitly zeroed below when we restore from
- // the parent held count (which has to be zero).
- __ str(zr, Address(rthread, JavaThread::jni_monitor_count_offset()));
-
- __ bind(L_skip_vthread_code);
- }
-#ifdef ASSERT
- else {
- // Check if this is a virtual thread continuation
- Label L_skip_vthread_code;
- __ ldrw(rscratch1, Address(sp, ContinuationEntry::flags_offset()));
- __ cbzw(rscratch1, L_skip_vthread_code);
-
- // See comment just above. If not checking JNI calls the JNI count is only
- // needed for assertion checking.
- __ str(zr, Address(rthread, JavaThread::jni_monitor_count_offset()));
-
- __ bind(L_skip_vthread_code);
- }
-#endif
-
- __ ldr(rscratch1, Address(sp, ContinuationEntry::parent_held_monitor_count_offset()));
- __ str(rscratch1, Address(rthread, JavaThread::held_monitor_count_offset()));
-
__ ldr(rscratch2, Address(sp, ContinuationEntry::parent_offset()));
__ str(rscratch2, Address(rthread, JavaThread::cont_entry_offset()));
__ add(rfp, sp, (int)ContinuationEntry::size());
diff --git a/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp b/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp
index f8f15741301..6c41e56b20b 100644
--- a/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp
+++ b/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2016 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -43,8 +43,6 @@ const bool CCallingConventionRequiresIntsAsLongs = true;
#define SUPPORTS_NATIVE_CX8
-#define SUPPORT_MONITOR_COUNT
-
// PPC64 is not specified as multi-copy-atomic
// So we must not #define CPU_MULTI_COPY_ATOMIC
diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
index aec36b3f3f9..9fe7e1f22ff 100644
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
@@ -1639,7 +1639,6 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj,
assert_different_registers(reg_cont_obj, reg_flags);
Register zero = R8_ARG6;
Register tmp2 = R9_ARG7;
- Register tmp3 = R10_ARG8;
DEBUG_ONLY(__ block_comment("fill {"));
#ifdef ASSERT
@@ -1655,12 +1654,9 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj,
__ stw(zero, in_bytes(ContinuationEntry::pin_count_offset()), R1_SP);
__ ld_ptr(tmp2, JavaThread::cont_fastpath_offset(), R16_thread);
- __ ld(tmp3, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread);
__ st_ptr(tmp2, ContinuationEntry::parent_cont_fastpath_offset(), R1_SP);
- __ std(tmp3, in_bytes(ContinuationEntry::parent_held_monitor_count_offset()), R1_SP);
__ st_ptr(zero, JavaThread::cont_fastpath_offset(), R16_thread);
- __ std(zero, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread);
DEBUG_ONLY(__ block_comment("} fill"));
}
@@ -1681,7 +1677,6 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj,
static void continuation_enter_cleanup(MacroAssembler* masm) {
Register tmp1 = R8_ARG6;
Register tmp2 = R9_ARG7;
- Register tmp3 = R10_ARG8;
#ifdef ASSERT
__ block_comment("clean {");
@@ -1692,57 +1687,8 @@ static void continuation_enter_cleanup(MacroAssembler* masm) {
__ ld_ptr(tmp1, ContinuationEntry::parent_cont_fastpath_offset(), R1_SP);
__ st_ptr(tmp1, JavaThread::cont_fastpath_offset(), R16_thread);
-
- if (CheckJNICalls) {
- // Check if this is a virtual thread continuation
- Label L_skip_vthread_code;
- __ lwz(R0, in_bytes(ContinuationEntry::flags_offset()), R1_SP);
- __ cmpwi(CR0, R0, 0);
- __ beq(CR0, L_skip_vthread_code);
-
- // If the held monitor count is > 0 and this vthread is terminating then
- // it failed to release a JNI monitor. So we issue the same log message
- // that JavaThread::exit does.
- __ ld(R0, in_bytes(JavaThread::jni_monitor_count_offset()), R16_thread);
- __ cmpdi(CR0, R0, 0);
- __ beq(CR0, L_skip_vthread_code);
-
- // Save return value potentially containing the exception oop
- Register ex_oop = R15_esp; // nonvolatile register
- __ mr(ex_oop, R3_RET);
- __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::log_jni_monitor_still_held));
- // Restore potental return value
- __ mr(R3_RET, ex_oop);
-
- // For vthreads we have to explicitly zero the JNI monitor count of the carrier
- // on termination. The held count is implicitly zeroed below when we restore from
- // the parent held count (which has to be zero).
- __ li(tmp1, 0);
- __ std(tmp1, in_bytes(JavaThread::jni_monitor_count_offset()), R16_thread);
-
- __ bind(L_skip_vthread_code);
- }
-#ifdef ASSERT
- else {
- // Check if this is a virtual thread continuation
- Label L_skip_vthread_code;
- __ lwz(R0, in_bytes(ContinuationEntry::flags_offset()), R1_SP);
- __ cmpwi(CR0, R0, 0);
- __ beq(CR0, L_skip_vthread_code);
-
- // See comment just above. If not checking JNI calls the JNI count is only
- // needed for assertion checking.
- __ li(tmp1, 0);
- __ std(tmp1, in_bytes(JavaThread::jni_monitor_count_offset()), R16_thread);
-
- __ bind(L_skip_vthread_code);
- }
-#endif
-
- __ ld(tmp2, in_bytes(ContinuationEntry::parent_held_monitor_count_offset()), R1_SP);
- __ ld_ptr(tmp3, ContinuationEntry::parent_offset(), R1_SP);
- __ std(tmp2, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread);
- __ st_ptr(tmp3, JavaThread::cont_entry_offset(), R16_thread);
+ __ ld_ptr(tmp2, ContinuationEntry::parent_offset(), R1_SP);
+ __ st_ptr(tmp2, JavaThread::cont_entry_offset(), R16_thread);
DEBUG_ONLY(__ block_comment("} clean"));
}
diff --git a/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp b/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp
index 407017ee1c0..57223cf4390 100644
--- a/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp
+++ b/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2015, Red Hat Inc. All rights reserved.
* Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -44,8 +44,6 @@ const bool CCallingConventionRequiresIntsAsLongs = false;
#define SUPPORTS_NATIVE_CX8
-#define SUPPORT_MONITOR_COUNT
-
#define SUPPORT_RESERVED_STACK_AREA
#define USE_POINTERS_TO_REGISTER_IMPL_ARRAY
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
index 5c85cc13bed..115b90a0087 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
@@ -225,36 +225,6 @@ void MacroAssembler::pop_cont_fastpath(Register java_thread) {
bind(done);
}
-void MacroAssembler::inc_held_monitor_count(Register tmp) {
- Address dst(xthread, JavaThread::held_monitor_count_offset());
- ld(tmp, dst);
- addi(tmp, tmp, 1);
- sd(tmp, dst);
-#ifdef ASSERT
- Label ok;
- test_bit(tmp, tmp, 63);
- beqz(tmp, ok);
- STOP("assert(held monitor count overflow)");
- should_not_reach_here();
- bind(ok);
-#endif
-}
-
-void MacroAssembler::dec_held_monitor_count(Register tmp) {
- Address dst(xthread, JavaThread::held_monitor_count_offset());
- ld(tmp, dst);
- subi(tmp, tmp, 1);
- sd(tmp, dst);
-#ifdef ASSERT
- Label ok;
- test_bit(tmp, tmp, 63);
- beqz(tmp, ok);
- STOP("assert(held monitor count underflow)");
- should_not_reach_here();
- bind(ok);
-#endif
-}
-
int MacroAssembler::align(int modulus, int extra_offset) {
CompressibleScope scope(this);
intptr_t before = offset();
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
index 13b70d5dbd7..9e713e90270 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
* Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -849,9 +849,6 @@ public:
void push_cont_fastpath(Register java_thread = xthread);
void pop_cont_fastpath(Register java_thread = xthread);
- void inc_held_monitor_count(Register tmp);
- void dec_held_monitor_count(Register tmp);
-
// if heap base register is used - reinit it with the correct value
void reinit_heapbase();
diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
index 94506e9f19d..b303178a666 100644
--- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
+++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
@@ -885,11 +885,8 @@ static void fill_continuation_entry(MacroAssembler* masm) {
__ ld(t0, Address(xthread, JavaThread::cont_fastpath_offset()));
__ sd(t0, Address(sp, ContinuationEntry::parent_cont_fastpath_offset()));
- __ ld(t0, Address(xthread, JavaThread::held_monitor_count_offset()));
- __ sd(t0, Address(sp, ContinuationEntry::parent_held_monitor_count_offset()));
__ sd(zr, Address(xthread, JavaThread::cont_fastpath_offset()));
- __ sd(zr, Address(xthread, JavaThread::held_monitor_count_offset()));
}
// on entry, sp points to the ContinuationEntry
@@ -905,50 +902,6 @@ static void continuation_enter_cleanup(MacroAssembler* masm) {
__ ld(t0, Address(sp, ContinuationEntry::parent_cont_fastpath_offset()));
__ sd(t0, Address(xthread, JavaThread::cont_fastpath_offset()));
-
- if (CheckJNICalls) {
- // Check if this is a virtual thread continuation
- Label L_skip_vthread_code;
- __ lwu(t0, Address(sp, ContinuationEntry::flags_offset()));
- __ beqz(t0, L_skip_vthread_code);
-
- // If the held monitor count is > 0 and this vthread is terminating then
- // it failed to release a JNI monitor. So we issue the same log message
- // that JavaThread::exit does.
- __ ld(t0, Address(xthread, JavaThread::jni_monitor_count_offset()));
- __ beqz(t0, L_skip_vthread_code);
-
- // Save return value potentially containing the exception oop in callee-saved x9
- __ mv(x9, x10);
- __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::log_jni_monitor_still_held));
- // Restore potential return value
- __ mv(x10, x9);
-
- // For vthreads we have to explicitly zero the JNI monitor count of the carrier
- // on termination. The held count is implicitly zeroed below when we restore from
- // the parent held count (which has to be zero).
- __ sd(zr, Address(xthread, JavaThread::jni_monitor_count_offset()));
-
- __ bind(L_skip_vthread_code);
- }
-#ifdef ASSERT
- else {
- // Check if this is a virtual thread continuation
- Label L_skip_vthread_code;
- __ lwu(t0, Address(sp, ContinuationEntry::flags_offset()));
- __ beqz(t0, L_skip_vthread_code);
-
- // See comment just above. If not checking JNI calls the JNI count is only
- // needed for assertion checking.
- __ sd(zr, Address(xthread, JavaThread::jni_monitor_count_offset()));
-
- __ bind(L_skip_vthread_code);
- }
-#endif
-
- __ ld(t0, Address(sp, ContinuationEntry::parent_held_monitor_count_offset()));
- __ sd(t0, Address(xthread, JavaThread::held_monitor_count_offset()));
-
__ ld(t0, Address(sp, ContinuationEntry::parent_offset()));
__ sd(t0, Address(xthread, JavaThread::cont_entry_offset()));
__ add(fp, sp, (int)ContinuationEntry::size() + 2 * wordSize /* 2 extra words to match up with leave() */);
diff --git a/src/hotspot/cpu/x86/globalDefinitions_x86.hpp b/src/hotspot/cpu/x86/globalDefinitions_x86.hpp
index 3c1474ae861..abbeb66a1ca 100644
--- a/src/hotspot/cpu/x86/globalDefinitions_x86.hpp
+++ b/src/hotspot/cpu/x86/globalDefinitions_x86.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2025, Oracle and/or its 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
@@ -34,8 +34,6 @@ const bool CCallingConventionRequiresIntsAsLongs = false;
#define SUPPORTS_NATIVE_CX8
-#define SUPPORT_MONITOR_COUNT
-
#define CPU_MULTI_COPY_ATOMIC
// The expected size in bytes of a cache line.
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
index 77ee71c0382..4f19b30b832 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
@@ -2431,14 +2431,6 @@ void MacroAssembler::pop_cont_fastpath() {
bind(L_done);
}
-void MacroAssembler::inc_held_monitor_count() {
- incrementq(Address(r15_thread, JavaThread::held_monitor_count_offset()));
-}
-
-void MacroAssembler::dec_held_monitor_count() {
- decrementq(Address(r15_thread, JavaThread::held_monitor_count_offset()));
-}
-
#ifdef ASSERT
void MacroAssembler::stop_if_in_cont(Register cont, const char* name) {
Label no_cont;
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
index 1c0dbaaefbe..ed1343d9c8c 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
@@ -472,9 +472,6 @@ class MacroAssembler: public Assembler {
void push_cont_fastpath();
void pop_cont_fastpath();
- void inc_held_monitor_count();
- void dec_held_monitor_count();
-
DEBUG_ONLY(void stop_if_in_cont(Register cont_reg, const char* name);)
// Round up to a power of two
diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
index e2a8f36b050..e702b587edd 100644
--- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
@@ -1352,11 +1352,8 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj,
__ movptr(rax, Address(r15_thread, JavaThread::cont_fastpath_offset()));
__ movptr(Address(rsp, ContinuationEntry::parent_cont_fastpath_offset()), rax);
- __ movq(rax, Address(r15_thread, JavaThread::held_monitor_count_offset()));
- __ movq(Address(rsp, ContinuationEntry::parent_held_monitor_count_offset()), rax);
__ movptr(Address(r15_thread, JavaThread::cont_fastpath_offset()), 0);
- __ movq(Address(r15_thread, JavaThread::held_monitor_count_offset()), 0);
}
//---------------------------- continuation_enter_cleanup ---------------------------
@@ -1380,49 +1377,6 @@ static void continuation_enter_cleanup(MacroAssembler* masm) {
#endif
__ movptr(rbx, Address(rsp, ContinuationEntry::parent_cont_fastpath_offset()));
__ movptr(Address(r15_thread, JavaThread::cont_fastpath_offset()), rbx);
-
- if (CheckJNICalls) {
- // Check if this is a virtual thread continuation
- Label L_skip_vthread_code;
- __ cmpl(Address(rsp, ContinuationEntry::flags_offset()), 0);
- __ jcc(Assembler::equal, L_skip_vthread_code);
-
- // If the held monitor count is > 0 and this vthread is terminating then
- // it failed to release a JNI monitor. So we issue the same log message
- // that JavaThread::exit does.
- __ cmpptr(Address(r15_thread, JavaThread::jni_monitor_count_offset()), 0);
- __ jcc(Assembler::equal, L_skip_vthread_code);
-
- // rax may hold an exception oop, save it before the call
- __ push(rax);
- __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::log_jni_monitor_still_held));
- __ pop(rax);
-
- // For vthreads we have to explicitly zero the JNI monitor count of the carrier
- // on termination. The held count is implicitly zeroed below when we restore from
- // the parent held count (which has to be zero).
- __ movq(Address(r15_thread, JavaThread::jni_monitor_count_offset()), 0);
-
- __ bind(L_skip_vthread_code);
- }
-#ifdef ASSERT
- else {
- // Check if this is a virtual thread continuation
- Label L_skip_vthread_code;
- __ cmpl(Address(rsp, ContinuationEntry::flags_offset()), 0);
- __ jcc(Assembler::equal, L_skip_vthread_code);
-
- // See comment just above. If not checking JNI calls the JNI count is only
- // needed for assertion checking.
- __ movq(Address(r15_thread, JavaThread::jni_monitor_count_offset()), 0);
-
- __ bind(L_skip_vthread_code);
- }
-#endif
-
- __ movq(rbx, Address(rsp, ContinuationEntry::parent_held_monitor_count_offset()));
- __ movq(Address(r15_thread, JavaThread::held_monitor_count_offset()), rbx);
-
__ movptr(rbx, Address(rsp, ContinuationEntry::parent_offset()));
__ movptr(Address(r15_thread, JavaThread::cont_entry_offset()), rbx);
__ addptr(rsp, checked_cast(ContinuationEntry::size()));
diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
index b4f033a4f9a..7ef16f6e32c 100644
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
@@ -255,7 +255,6 @@
nonstatic_field(JavaThread, _should_post_on_exceptions_flag, int) \
nonstatic_field(JavaThread, _jni_environment, JNIEnv) \
nonstatic_field(JavaThread, _stack_overflow_state._reserved_stack_activation, address) \
- nonstatic_field(JavaThread, _held_monitor_count, intx) \
nonstatic_field(JavaThread, _lock_stack, LockStack) \
nonstatic_field(JavaThread, _om_cache, OMCache) \
nonstatic_field(JavaThread, _cont_entry, ContinuationEntry*) \
diff --git a/src/hotspot/share/runtime/continuation.hpp b/src/hotspot/share/runtime/continuation.hpp
index e678e0bd42b..0cfd484361d 100644
--- a/src/hotspot/share/runtime/continuation.hpp
+++ b/src/hotspot/share/runtime/continuation.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, Oracle and/or its 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
@@ -53,10 +53,9 @@ enum freeze_result {
freeze_ok_bottom = 1,
freeze_pinned_cs = 2,
freeze_pinned_native = 3,
- freeze_pinned_monitor = 4,
- freeze_exception = 5,
- freeze_not_mounted = 6,
- freeze_unsupported = 7
+ freeze_exception = 4,
+ freeze_not_mounted = 5,
+ freeze_unsupported = 6
};
class Continuation : AllStatic {
diff --git a/src/hotspot/share/runtime/continuationEntry.cpp b/src/hotspot/share/runtime/continuationEntry.cpp
index 4551bfa7cc8..69a20808798 100644
--- a/src/hotspot/share/runtime/continuationEntry.cpp
+++ b/src/hotspot/share/runtime/continuationEntry.cpp
@@ -107,7 +107,6 @@ void ContinuationEntry::describe(FrameValues& values, int frame_no) const {
values.describe(frame_no, (intptr_t*)(usp + in_bytes(ContinuationEntry::argsize_offset())), "argsize");
values.describe(frame_no, (intptr_t*)(usp + in_bytes(ContinuationEntry::pin_count_offset())), "pin_count");
values.describe(frame_no, (intptr_t*)(usp + in_bytes(ContinuationEntry::parent_cont_fastpath_offset())), "parent fastpath");
- values.describe(frame_no, (intptr_t*)(usp + in_bytes(ContinuationEntry::parent_held_monitor_count_offset())), "parent held monitor count");
}
#endif
diff --git a/src/hotspot/share/runtime/continuationEntry.hpp b/src/hotspot/share/runtime/continuationEntry.hpp
index 3c8532b9e87..8361f2f912b 100644
--- a/src/hotspot/share/runtime/continuationEntry.hpp
+++ b/src/hotspot/share/runtime/continuationEntry.hpp
@@ -79,11 +79,6 @@ class ContinuationEntry {
// The caller (if there is one) is the still frozen top frame in the StackChunk.
int _argsize;
intptr_t* _parent_cont_fastpath;
-#ifdef _LP64
- int64_t _parent_held_monitor_count;
-#else
- int32_t _parent_held_monitor_count;
-#endif
uint32_t _pin_count;
public:
@@ -94,7 +89,6 @@ class ContinuationEntry {
static ByteSize argsize_offset() { return byte_offset_of(ContinuationEntry, _argsize); }
static ByteSize pin_count_offset(){ return byte_offset_of(ContinuationEntry, _pin_count); }
static ByteSize parent_cont_fastpath_offset() { return byte_offset_of(ContinuationEntry, _parent_cont_fastpath); }
- static ByteSize parent_held_monitor_count_offset() { return byte_offset_of(ContinuationEntry, _parent_held_monitor_count); }
static address return_pc() { return _return_pc; }
static address return_pc_address() { return (address)&_return_pc; }
@@ -103,7 +97,6 @@ class ContinuationEntry {
static size_t size() { return align_up((int)sizeof(ContinuationEntry), 2*wordSize); }
ContinuationEntry* parent() const { return _parent; }
- int64_t parent_held_monitor_count() const { return (int64_t)_parent_held_monitor_count; }
static address entry_pc() { return _return_pc; }
intptr_t* entry_sp() const { return (intptr_t*)this; }
diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
index 024b69c765f..33b4f2bf488 100644
--- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp
+++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
@@ -1736,13 +1736,10 @@ static inline freeze_result freeze_internal(JavaThread* current, intptr_t* const
assert(entry->is_virtual_thread() == (entry->scope(current) == java_lang_VirtualThread::vthread_scope()), "");
- assert((current->held_monitor_count() == 0 && current->jni_monitor_count() == 0),
- "Held monitor count should not be used for lightweight locking: " INT64_FORMAT " JNI: " INT64_FORMAT, (int64_t)current->held_monitor_count(), (int64_t)current->jni_monitor_count());
-
- if (entry->is_pinned() || current->held_monitor_count() > 0) {
- log_develop_debug(continuations)("PINNED due to critical section/hold monitor");
+ if (entry->is_pinned()) {
+ log_develop_debug(continuations)("PINNED due to critical section");
verify_continuation(cont.continuation());
- freeze_result res = entry->is_pinned() ? freeze_pinned_cs : freeze_pinned_monitor;
+ const freeze_result res = freeze_pinned_cs;
if (!preempt) {
JFR_ONLY(current->set_last_freeze_fail_result(res);)
}
@@ -1799,8 +1796,6 @@ static freeze_result is_pinned0(JavaThread* thread, oop cont_scope, bool safepoi
}
if (entry->is_pinned()) {
return freeze_pinned_cs;
- } else if (thread->held_monitor_count() > 0) {
- return freeze_pinned_monitor;
}
RegisterMap map(thread,
@@ -1836,15 +1831,12 @@ static freeze_result is_pinned0(JavaThread* thread, oop cont_scope, bool safepoi
if (scope == cont_scope) {
break;
}
- intx monitor_count = entry->parent_held_monitor_count();
entry = entry->parent();
if (entry == nullptr) {
break;
}
if (entry->is_pinned()) {
return freeze_pinned_cs;
- } else if (monitor_count > 0) {
- return freeze_pinned_monitor;
}
}
}
diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp
index e5af8d7bedd..8bb8095878f 100644
--- a/src/hotspot/share/runtime/javaThread.cpp
+++ b/src/hotspot/share/runtime/javaThread.cpp
@@ -488,8 +488,6 @@ JavaThread::JavaThread(MemTag mem_tag) :
_cont_entry(nullptr),
_cont_fastpath(nullptr),
_cont_fastpath_thread_state(1),
- _held_monitor_count(0),
- _jni_monitor_count(0),
_unlocked_inflated_monitor(nullptr),
_preempt_alternate_return(nullptr),
@@ -927,27 +925,6 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
"should not have a Java frame when detaching or exiting");
ObjectSynchronizer::release_monitors_owned_by_thread(this);
assert(!this->has_pending_exception(), "release_monitors should have cleared");
- // Check for monitor counts being out of sync.
- assert(held_monitor_count() == jni_monitor_count(),
- "held monitor count should be equal to jni: %zd != %zd",
- held_monitor_count(), jni_monitor_count());
- // All in-use monitors, including JNI-locked ones, should have been released above.
- assert(held_monitor_count() == 0, "Failed to unlock %zd object monitors",
- held_monitor_count());
- } else {
- // Check for monitor counts being out of sync.
- assert(held_monitor_count() == jni_monitor_count(),
- "held monitor count should be equal to jni: %zd != %zd",
- held_monitor_count(), jni_monitor_count());
- // It is possible that a terminating thread failed to unlock monitors it locked
- // via JNI so we don't assert the count is zero.
- }
-
- if (CheckJNICalls && jni_monitor_count() > 0) {
- // We would like a fatal here, but due to we never checked this before there
- // is a lot of tests which breaks, even with an error log.
- log_debug(jni)("JavaThread %s (tid: %zu) with Objects still locked by JNI MonitorEnter.",
- exit_type == JavaThread::normal_exit ? "exiting" : "detaching", os::current_thread_id());
}
// These things needs to be done while we are still a Java Thread. Make sure that thread
@@ -1988,26 +1965,6 @@ void JavaThread::trace_stack() {
#endif // PRODUCT
-// Slow-path increment of the held monitor counts. JNI locking is always
-// this slow-path.
-void JavaThread::inc_held_monitor_count(intx i, bool jni) {
-#ifdef SUPPORT_MONITOR_COUNT
- // Nothing to do. Just do some sanity check.
- assert(_held_monitor_count == 0, "counter should not be used");
- assert(_jni_monitor_count == 0, "counter should not be used");
-#endif // SUPPORT_MONITOR_COUNT
-}
-
-// Slow-path decrement of the held monitor counts. JNI unlocking is always
-// this slow-path.
-void JavaThread::dec_held_monitor_count(intx i, bool jni) {
-#ifdef SUPPORT_MONITOR_COUNT
- // Nothing to do. Just do some sanity check.
- assert(_held_monitor_count == 0, "counter should not be used");
- assert(_jni_monitor_count == 0, "counter should not be used");
-#endif // SUPPORT_MONITOR_COUNT
-}
-
frame JavaThread::vthread_last_frame() {
assert (is_vthread_mounted(), "Virtual thread not mounted");
return last_frame();
diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp
index 89c3191669a..c8be1594a69 100644
--- a/src/hotspot/share/runtime/javaThread.hpp
+++ b/src/hotspot/share/runtime/javaThread.hpp
@@ -476,9 +476,6 @@ class JavaThread: public Thread {
// frame inside the continuation that we know about
int _cont_fastpath_thread_state; // whether global thread state allows continuation fastpath (JVMTI)
- // It's signed for error detection.
- intx _held_monitor_count; // used by continuations for fast lock detection
- intx _jni_monitor_count;
ObjectMonitor* _unlocked_inflated_monitor;
// This is the field we poke in the interpreter and native
@@ -662,13 +659,6 @@ private:
bool cont_fastpath() const { return _cont_fastpath == nullptr && _cont_fastpath_thread_state != 0; }
bool cont_fastpath_thread_state() const { return _cont_fastpath_thread_state != 0; }
- void inc_held_monitor_count(intx i = 1, bool jni = false);
- void dec_held_monitor_count(intx i = 1, bool jni = false);
-
- intx held_monitor_count() { return _held_monitor_count; }
- intx jni_monitor_count() { return _jni_monitor_count; }
- void clear_jni_monitor_count() { _jni_monitor_count = 0; }
-
// Support for SharedRuntime::monitor_exit_helper()
ObjectMonitor* unlocked_inflated_monitor() const { return _unlocked_inflated_monitor; }
void clear_unlocked_inflated_monitor() {
@@ -897,8 +887,6 @@ public:
static ByteSize cont_entry_offset() { return byte_offset_of(JavaThread, _cont_entry); }
static ByteSize cont_fastpath_offset() { return byte_offset_of(JavaThread, _cont_fastpath); }
- static ByteSize held_monitor_count_offset() { return byte_offset_of(JavaThread, _held_monitor_count); }
- static ByteSize jni_monitor_count_offset() { return byte_offset_of(JavaThread, _jni_monitor_count); }
static ByteSize preemption_cancelled_offset() { return byte_offset_of(JavaThread, _preemption_cancelled); }
static ByteSize preempt_alternate_return_offset() { return byte_offset_of(JavaThread, _preempt_alternate_return); }
static ByteSize unlocked_inflated_monitor_offset() { return byte_offset_of(JavaThread, _unlocked_inflated_monitor); }
diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp
index 8859f6e7f5f..4c00ecac697 100644
--- a/src/hotspot/share/runtime/objectMonitor.cpp
+++ b/src/hotspot/share/runtime/objectMonitor.cpp
@@ -1952,7 +1952,6 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
int relock_count = JvmtiDeferredUpdates::get_and_reset_relock_count_after_wait(current);
_recursions = save // restore the old recursion count
+ relock_count; // increased by the deferred relock count
- current->inc_held_monitor_count(relock_count); // Deopt never entered these counts.
_waiters--; // decrement the number of waiters
// Verify a few postconditions
diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
index cf949fe1e7c..85aeba361ff 100644
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -1980,7 +1980,6 @@ void SharedRuntime::monitor_exit_helper(oopDesc* obj, BasicLock* lock, JavaThrea
if (!m->try_enter(current, /*check_for_recursion*/ false)) {
// Some other thread acquired the lock (or the monitor was
// deflated). Either way we are done.
- current->dec_held_monitor_count();
return;
}
}
@@ -2002,20 +2001,6 @@ JRT_LEAF(void, SharedRuntime::complete_monitor_unlocking_C(oopDesc* obj, BasicLo
SharedRuntime::monitor_exit_helper(obj, lock, current);
JRT_END
-// This is only called when CheckJNICalls is true, and only
-// for virtual thread termination.
-JRT_LEAF(void, SharedRuntime::log_jni_monitor_still_held())
- assert(CheckJNICalls, "Only call this when checking JNI usage");
- if (log_is_enabled(Debug, jni)) {
- JavaThread* current = JavaThread::current();
- int64_t vthread_id = java_lang_Thread::thread_id(current->vthread());
- int64_t carrier_id = java_lang_Thread::thread_id(current->threadObj());
- log_debug(jni)("VirtualThread (tid: " INT64_FORMAT ", carrier id: " INT64_FORMAT
- ") exiting with Objects still locked by JNI MonitorEnter.",
- vthread_id, carrier_id);
- }
-JRT_END
-
#ifndef PRODUCT
void SharedRuntime::print_statistics() {
diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp
index 6544e380d99..2a19b80c3b5 100644
--- a/src/hotspot/share/runtime/sharedRuntime.hpp
+++ b/src/hotspot/share/runtime/sharedRuntime.hpp
@@ -404,9 +404,6 @@ class SharedRuntime: AllStatic {
static void monitor_exit_helper(oopDesc* obj, BasicLock* lock, JavaThread* current);
- // Issue UL warning for unlocked JNI monitor on virtual thread termination
- static void log_jni_monitor_still_held();
-
private:
static Handle find_callee_info(Bytecodes::Code& bc, CallInfo& callinfo, TRAPS);
static Handle find_callee_info_helper(vframeStream& vfst, Bytecodes::Code& bc, CallInfo& callinfo, TRAPS);
diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp
index ff4e09e741f..e513c57fe06 100644
--- a/src/hotspot/share/runtime/synchronizer.cpp
+++ b/src/hotspot/share/runtime/synchronizer.cpp
@@ -452,7 +452,6 @@ void ObjectSynchronizer::jni_enter(Handle obj, JavaThread* current) {
while (true) {
BasicLock lock;
if (LightweightSynchronizer::inflate_and_enter(obj(), &lock, inflate_cause_jni_enter, current, current) != nullptr) {
- current->inc_held_monitor_count(1, true);
break;
}
}
@@ -470,7 +469,6 @@ void ObjectSynchronizer::jni_exit(oop obj, TRAPS) {
// monitor even if an exception was already pending.
if (monitor->check_owner(THREAD)) {
monitor->exit(current);
- current->dec_held_monitor_count(1, true);
}
}
@@ -1263,8 +1261,7 @@ class ReleaseJavaMonitorsClosure: public MonitorClosure {
public:
ReleaseJavaMonitorsClosure(JavaThread* thread) : _thread(thread) {}
void do_monitor(ObjectMonitor* mid) {
- intx rec = mid->complete_exit(_thread);
- _thread->dec_held_monitor_count(rec + 1);
+ mid->complete_exit(_thread);
}
};
@@ -1290,9 +1287,6 @@ void ObjectSynchronizer::release_monitors_owned_by_thread(JavaThread* current) {
ObjectSynchronizer::owned_monitors_iterate(&rjmc, current);
assert(!current->has_pending_exception(), "Should not be possible");
current->clear_pending_exception();
- assert(current->held_monitor_count() == 0, "Should not be possible");
- // All monitors (including entered via JNI) have been unlocked above, so we need to clear jni count.
- current->clear_jni_monitor_count();
}
const char* ObjectSynchronizer::inflate_cause_name(const InflateCause cause) {
diff --git a/src/hotspot/share/runtime/synchronizer.inline.hpp b/src/hotspot/share/runtime/synchronizer.inline.hpp
index 6a850e5c8ca..cdbeb1daf5b 100644
--- a/src/hotspot/share/runtime/synchronizer.inline.hpp
+++ b/src/hotspot/share/runtime/synchronizer.inline.hpp
@@ -61,8 +61,6 @@ inline bool ObjectSynchronizer::quick_enter(oop obj, BasicLock* lock, JavaThread
}
inline void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) {
- current->dec_held_monitor_count();
-
LightweightSynchronizer::exit(object, lock, current);
}
diff --git a/src/java.base/share/classes/jdk/internal/vm/Continuation.java b/src/java.base/share/classes/jdk/internal/vm/Continuation.java
index a97f9ac9ea4..a7eb3ea6a9f 100644
--- a/src/java.base/share/classes/jdk/internal/vm/Continuation.java
+++ b/src/java.base/share/classes/jdk/internal/vm/Continuation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, Oracle and/or its 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
@@ -57,7 +57,6 @@ public class Continuation {
/** Reason for pinning */
public enum Pinned {
/** Native frame on stack */ NATIVE,
- /** Monitor held */ MONITOR,
/** In critical section */ CRITICAL_SECTION,
/** Exception (OOME/SOE) */ EXCEPTION
}
@@ -69,8 +68,7 @@ public class Continuation {
/** Permanent failure: continuation already yielding */ PERM_FAIL_YIELDING(null),
/** Permanent failure: continuation not mounted on the thread */ PERM_FAIL_NOT_MOUNTED(null),
/** Transient failure: continuation pinned due to a held CS */ TRANSIENT_FAIL_PINNED_CRITICAL_SECTION(Pinned.CRITICAL_SECTION),
- /** Transient failure: continuation pinned due to native frame */ TRANSIENT_FAIL_PINNED_NATIVE(Pinned.NATIVE),
- /** Transient failure: continuation pinned due to a held monitor */ TRANSIENT_FAIL_PINNED_MONITOR(Pinned.MONITOR);
+ /** Transient failure: continuation pinned due to native frame */ TRANSIENT_FAIL_PINNED_NATIVE(Pinned.NATIVE);
final Pinned pinned;
private PreemptStatus(Pinned reason) { this.pinned = reason; }
@@ -85,8 +83,7 @@ public class Continuation {
return switch (reason) {
case 2 -> Pinned.CRITICAL_SECTION;
case 3 -> Pinned.NATIVE;
- case 4 -> Pinned.MONITOR;
- case 5 -> Pinned.EXCEPTION;
+ case 4 -> Pinned.EXCEPTION;
default -> throw new AssertionError("Unknown pinned reason: " + reason);
};
}
From 59e87437b4f9259121710dca5e595ca714c3e71b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roberto=20Casta=C3=B1eda=20Lozano?=
Date: Mon, 6 Oct 2025 08:14:24 +0000
Subject: [PATCH 028/160] 8368753: IGV: improve CFG view of difference graphs
Reviewed-by: chagedorn, mhaessig, dfenacci
---
.../com/sun/hotspot/igv/data/InputBlock.java | 2 +-
.../hotspot/igv/data/services/Scheduler.java | 10 ++--
.../hotspot/igv/difference/Difference.java | 9 ++++
.../ServerCompilerScheduler.java | 54 +++++++++++++------
4 files changed, 54 insertions(+), 21 deletions(-)
diff --git a/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputBlock.java b/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputBlock.java
index 6670470b5e8..a5b282f9de1 100644
--- a/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputBlock.java
+++ b/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputBlock.java
@@ -135,7 +135,7 @@ public class InputBlock {
successors.add(b);
}
- void setArtificial() {
+ public void setArtificial() {
this.artificial = true;
}
diff --git a/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/services/Scheduler.java b/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/services/Scheduler.java
index 1fe6ac3342a..4ff960f7c3d 100644
--- a/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/services/Scheduler.java
+++ b/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/services/Scheduler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2025, Oracle and/or its 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
@@ -24,9 +24,7 @@
*/
package com.sun.hotspot.igv.data.services;
-import com.sun.hotspot.igv.data.InputBlock;
import com.sun.hotspot.igv.data.InputGraph;
-import java.util.Collection;
/**
*
@@ -34,5 +32,9 @@ import java.util.Collection;
*/
public interface Scheduler {
- public Collection schedule(InputGraph graph);
+ // Compute a set of scheduled blocks for the given graph, creating new
+ // blocks if these are not found in the graph.
+ public void schedule(InputGraph graph);
+ // Schedule locally the set of blocks in the given graph.
+ public void scheduleLocally(InputGraph graph);
}
diff --git a/src/utils/IdealGraphVisualizer/Difference/src/main/java/com/sun/hotspot/igv/difference/Difference.java b/src/utils/IdealGraphVisualizer/Difference/src/main/java/com/sun/hotspot/igv/difference/Difference.java
index 89b1434663f..e3888be31be 100644
--- a/src/utils/IdealGraphVisualizer/Difference/src/main/java/com/sun/hotspot/igv/difference/Difference.java
+++ b/src/utils/IdealGraphVisualizer/Difference/src/main/java/com/sun/hotspot/igv/difference/Difference.java
@@ -114,6 +114,9 @@ public class Difference {
Map blocksMap = new HashMap<>();
for (InputBlock blk : a.getBlocks()) {
InputBlock diffblk = graph.addBlock(blk.getName());
+ if (blk.isArtificial()) {
+ diffblk.setArtificial();
+ }
blocksMap.put(blk, diffblk);
}
for (InputBlock blk : b.getBlocks()) {
@@ -121,6 +124,9 @@ public class Difference {
if (diffblk == null) {
diffblk = graph.addBlock(blk.getName());
}
+ if (blk.isArtificial()) {
+ diffblk.setArtificial();
+ }
blocksMap.put(blk, diffblk);
}
@@ -249,6 +255,9 @@ public class Difference {
}
}
+ Scheduler s = Lookup.getDefault().lookup(Scheduler.class);
+ s.scheduleLocally(graph);
+
return graph;
}
diff --git a/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/java/com/sun/hotspot/igv/servercompiler/ServerCompilerScheduler.java b/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/java/com/sun/hotspot/igv/servercompiler/ServerCompilerScheduler.java
index 0a069c53a71..93199ea35a1 100644
--- a/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/java/com/sun/hotspot/igv/servercompiler/ServerCompilerScheduler.java
+++ b/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/java/com/sun/hotspot/igv/servercompiler/ServerCompilerScheduler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2025, Oracle and/or its 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
@@ -296,10 +296,25 @@ public class ServerCompilerScheduler implements Scheduler {
return n.getProperties().get("block");
}
+ private boolean initialize(InputGraph graph) {
+ nodes = new ArrayList<>();
+ inputNodeToNode = new HashMap<>(graph.getNodes().size());
+ this.graph = graph;
+ if (!hasCategoryInformation()) {
+ ErrorManager.getDefault().log(ErrorManager.WARNING,
+ "Cannot find node category information in the input graph. " +
+ "The control-flow graph will not be approximated.");
+ return false;
+ }
+ buildUpGraph();
+ markCFGNodes();
+ return true;
+ }
+
@Override
- public Collection schedule(InputGraph graph) {
+ public void schedule(InputGraph graph) {
if (graph.getNodes().isEmpty()) {
- return Collections.emptyList();
+ return;
}
if (graph.getBlocks().size() > 0) {
@@ -311,20 +326,11 @@ public class ServerCompilerScheduler implements Scheduler {
assert graph.getBlock(n) != null;
}
}
- return graph.getBlocks();
+ return;
} else {
- nodes = new ArrayList<>();
- inputNodeToNode = new HashMap<>(graph.getNodes().size());
-
- this.graph = graph;
- if (!hasCategoryInformation()) {
- ErrorManager.getDefault().log(ErrorManager.WARNING,
- "Cannot find node category information in the input graph. " +
- "The control-flow graph will not be approximated.");
- return null;
+ if (!initialize(graph)) {
+ return;
}
- buildUpGraph();
- markCFGNodes();
buildBlocks();
schedulePinned();
buildDominators();
@@ -333,10 +339,26 @@ public class ServerCompilerScheduler implements Scheduler {
check();
reportWarnings();
- return blocks;
+ return;
}
}
+ @Override
+ public void scheduleLocally(InputGraph graph) {
+ if (!initialize(graph)) {
+ return;
+ }
+ // Import global schedule from the given graph.
+ blocks = new Vector<>();
+ for (InputBlock block : graph.getBlocks()) {
+ blocks.add(block);
+ for (InputNode in : block.getNodes()) {
+ inputNodeToNode.get(in).block = block;
+ }
+ }
+ scheduleLocal();
+ }
+
private void scheduleLocal() {
// Leave only local predecessors and successors.
for (InputBlock b : blocks) {
From baf8bc5701c43425e3345f82d4318b134b26d7c9 Mon Sep 17 00:00:00 2001
From: Francesco Andreuzzi
Date: Mon, 6 Oct 2025 08:14:44 +0000
Subject: [PATCH 029/160] 8369038: Parallel: Use NMethodMarkingScope and
ThreadsClaimTokenScope in psParallelCompact
Reviewed-by: ayang, shade
---
src/hotspot/share/gc/parallel/psParallelCompact.cpp | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
index af812c652a6..5affa1a3e35 100644
--- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp
+++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
@@ -28,6 +28,7 @@
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "code/codeCache.hpp"
+#include "code/nmethod.hpp"
#include "compiler/oopMap.hpp"
#include "gc/parallel/objectStartArray.inline.hpp"
#include "gc/parallel/parallelArguments.hpp"
@@ -61,7 +62,6 @@
#include "gc/shared/referenceProcessor.hpp"
#include "gc/shared/referenceProcessorPhaseTimes.hpp"
#include "gc/shared/spaceDecorator.hpp"
-#include "gc/shared/strongRootsScope.hpp"
#include "gc/shared/taskTerminator.hpp"
#include "gc/shared/weakProcessor.inline.hpp"
#include "gc/shared/workerPolicy.hpp"
@@ -1085,7 +1085,8 @@ void steal_marking_work(TaskTerminator& terminator, uint worker_id) {
}
class MarkFromRootsTask : public WorkerTask {
- StrongRootsScope _strong_roots_scope; // needed for Threads::possibly_parallel_threads_do
+ NMethodMarkingScope _nmethod_marking_scope;
+ ThreadsClaimTokenScope _threads_claim_token_scope;
OopStorageSetStrongParState _oop_storage_set_par_state;
TaskTerminator _terminator;
uint _active_workers;
@@ -1093,7 +1094,8 @@ class MarkFromRootsTask : public WorkerTask {
public:
MarkFromRootsTask(uint active_workers) :
WorkerTask("MarkFromRootsTask"),
- _strong_roots_scope(active_workers),
+ _nmethod_marking_scope(),
+ _threads_claim_token_scope(),
_terminator(active_workers, ParCompactionManager::marking_stacks()),
_active_workers(active_workers) {}
From 2c114d676d9904094dd6058d15f06d801ec7a3d6 Mon Sep 17 00:00:00 2001
From: SendaoYan
Date: Mon, 6 Oct 2025 09:26:51 +0000
Subject: [PATCH 030/160] 8367899:
compiler/c2/gvn/TestBitCompressValueTransform.java intermittent timed out
Reviewed-by: dfenacci, chagedorn
---
.../c2/gvn/TestBitCompressValueTransform.java | 49 ++++++++-----------
1 file changed, 20 insertions(+), 29 deletions(-)
diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java b/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java
index 98f26f120b1..3f32f78871c 100644
--- a/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java
+++ b/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java
@@ -76,10 +76,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test1")
public void run1(RunInfo info) {
- long res = 0;
- for (int i = 0; i < 10000; i++) {
- res |= test1(field_L);
- }
+ long res = test1(field_L);
Asserts.assertEQ(res, gold_L);
}
@@ -92,10 +89,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test2")
public void run2(RunInfo info) {
- int res = 0;
- for (int i = 0; i < 10000; i++) {
- res |= test2(field_I);
- }
+ int res = test2(field_I);
Asserts.assertEQ(res, gold_I);
}
@@ -113,7 +107,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test3")
public void run3(RunInfo info) {
int res = 0;
- for (int i = 1; i < 10000; i++) {
+ for (int i = 1; i < 100; i++) {
res |= test3(i);
}
Asserts.assertLTE(0, res);
@@ -133,7 +127,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test4")
public void run4(RunInfo info) {
long res = 0;
- for (long i = 1; i < 10000; i++) {
+ for (long i = 1; i < 100; i++) {
res |= test4(i);
}
Asserts.assertLTE(0L, res);
@@ -151,7 +145,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test5")
public void run5(RunInfo info) {
long res = 0;
- for (int i = -10000; i < 10000; i++) {
+ for (int i = -100; i < 100; i++) {
res |= test5((long)i);
}
Asserts.assertEQ(-1L, res);
@@ -169,7 +163,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test6")
public void run6(RunInfo info) {
long res = 0;
- for (int i = -10000; i < 10000; i++) {
+ for (int i = -100; i < 100; i++) {
res |= test6((long)i);
}
Asserts.assertLTE(0L, res);
@@ -188,7 +182,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test7")
public void run7(RunInfo info) {
long res = Long.MIN_VALUE;
- for (int i = -10000; i < 10000; i++) {
+ for (int i = -100; i < 100; i++) {
res = Long.max(test7((long)i), res);
}
Asserts.assertGTE(10000L, res);
@@ -206,7 +200,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test8")
public void run8(RunInfo info) {
int res = 0;
- for (int i = -10000; i < 10000; i++) {
+ for (int i = -100; i < 100; i++) {
res |= test8(i);
}
Asserts.assertEQ(-1, res);
@@ -224,7 +218,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test9")
public void run9(RunInfo info) {
int res = 0;
- for (int i = -10000; i < 10000; i++) {
+ for (int i = -100; i < 100; i++) {
res |= test9(i);
}
Asserts.assertLTE(0, res);
@@ -243,10 +237,10 @@ public class TestBitCompressValueTransform {
@Run(test = "test10")
public void run10(RunInfo info) {
int res = Integer.MIN_VALUE;
- for (int i = -10000; i < 10000; i++) {
+ for (int i = -100; i < 100; i++) {
res = Integer.max(test10(i), res);
}
- Asserts.assertGTE(10000, res);
+ Asserts.assertGTE(100, res);
}
@Test
@@ -260,7 +254,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test11")
public void run11(RunInfo info) {
int res = 0;
- for (int i = -10000; i < 10000; i++) {
+ for (int i = -100; i < 100; i++) {
res |= test11(i);
}
Asserts.assertEQ(0, res);
@@ -277,7 +271,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test12")
public void run12(RunInfo info) {
long res = 0;
- for (int i = -10000; i < 10000; i++) {
+ for (int i = -100; i < 100; i++) {
res |= test12(i);
}
Asserts.assertEQ(0L, res);
@@ -294,7 +288,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test13")
public void run13(RunInfo info) {
int res = 0;
- for (int i = -10000; i < 10000; i++) {
+ for (int i = -100; i < 100; i++) {
res |= test13(i);
}
Asserts.assertEQ(0, res);
@@ -311,7 +305,7 @@ public class TestBitCompressValueTransform {
@Run(test = "test14")
public void run14(RunInfo info) {
long res = 0;
- for (int i = -10000; i < 10000; i++) {
+ for (int i = -100; i < 100; i++) {
res |= test14(i);
}
Asserts.assertEQ(0L, res);
@@ -327,10 +321,7 @@ public class TestBitCompressValueTransform {
@Run (test = "test15")
public void run15(RunInfo info) {
- int res = 0;
- for (int i = 0; i < 10000; i++) {
- res |= test15(0, 0);
- }
+ int res = test15(0, 0);
Asserts.assertEQ(0, res);
}
@@ -408,7 +399,7 @@ public class TestBitCompressValueTransform {
int actual = 0;
int expected = 0;
- for (int i = 0; i < 10000; i++) {
+ for (int i = 0; i < 100; i++) {
int arg1 = GEN_I.next();
int arg2 = GEN_I.next();
@@ -492,7 +483,7 @@ public class TestBitCompressValueTransform {
int actual = 0;
int expected = 0;
- for (int i = 0; i < 10000; i++) {
+ for (int i = 0; i < 100; i++) {
int arg1 = GEN_I.next();
int arg2 = GEN_I.next();
@@ -576,7 +567,7 @@ public class TestBitCompressValueTransform {
long actual = 0;
long expected = 0;
- for (int i = 0; i < 10000; i++) {
+ for (int i = 0; i < 100; i++) {
long arg1 = GEN_L.next();
long arg2 = GEN_L.next();
@@ -660,7 +651,7 @@ public class TestBitCompressValueTransform {
long actual = 0;
long expected = 0;
- for (int i = 0; i < 10000; i++) {
+ for (int i = 0; i < 100; i++) {
long arg1 = GEN_L.next();
long arg2 = GEN_L.next();
From 2bfada3f58df6c041d948267368cbc4db915cac3 Mon Sep 17 00:00:00 2001
From: jonghoonpark
Date: Mon, 6 Oct 2025 11:53:14 +0000
Subject: [PATCH 031/160] 8364927: Add @requires annotation to
TestReclaimStringsLeaksMemory.java
Reviewed-by: tschatzl, stefank, ayang
---
.../stress/TestReclaimStringsLeaksMemory.java | 49 +++++++++++++++++--
1 file changed, 45 insertions(+), 4 deletions(-)
diff --git a/test/hotspot/jtreg/gc/stress/TestReclaimStringsLeaksMemory.java b/test/hotspot/jtreg/gc/stress/TestReclaimStringsLeaksMemory.java
index 80ab9e21667..1f19222fb8e 100644
--- a/test/hotspot/jtreg/gc/stress/TestReclaimStringsLeaksMemory.java
+++ b/test/hotspot/jtreg/gc/stress/TestReclaimStringsLeaksMemory.java
@@ -24,19 +24,60 @@
package gc.stress;
/*
- * @test TestReclaimStringsLeaksMemory
+ * @test id=Serial
* @bug 8180048
- * @summary Ensure that during a Full GC interned string memory is reclaimed completely.
- * @requires vm.gc == "null"
+ * @summary Ensure that during a Full GC interned string memory is reclaimed completely with SerialGC.
+ * @requires vm.gc.Serial
* @requires !vm.debug
* @library /test/lib
* @modules java.base/jdk.internal.misc
- * @run driver/timeout=480 gc.stress.TestReclaimStringsLeaksMemory
* @run driver/timeout=480 gc.stress.TestReclaimStringsLeaksMemory -XX:+UseSerialGC
+ */
+
+/*
+ * @test id=Parallel
+ * @bug 8180048
+ * @summary Ensure that during a Full GC interned string memory is reclaimed completely with ParallelGC.
+ * @requires vm.gc.Parallel
+ * @requires !vm.debug
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
* @run driver/timeout=480 gc.stress.TestReclaimStringsLeaksMemory -XX:+UseParallelGC
+ */
+
+/*
+ * @test id=G1
+ * @bug 8180048
+ * @summary Ensure that during a Full GC interned string memory is reclaimed completely with G1GC.
+ * @requires vm.gc.G1
+ * @requires !vm.debug
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
* @run driver/timeout=480 gc.stress.TestReclaimStringsLeaksMemory -XX:+UseG1GC
*/
+/*
+ * @test id=Shenandoah
+ * @bug 8180048
+ * @summary Ensure that during a Full GC interned string memory is reclaimed completely with ShenandoahGC.
+ * @requires vm.gc.Shenandoah
+ * @requires !vm.debug
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @run driver/timeout=480 gc.stress.TestReclaimStringsLeaksMemory -XX:+UseShenandoahGC
+ */
+
+/*
+ * @test id=Z
+ * @bug 8180048
+ * @summary Ensure that during a Full GC interned string memory is reclaimed completely with ZGC.
+ * @requires vm.gc.Z
+ * @requires !vm.debug
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @run driver/timeout=480 gc.stress.TestReclaimStringsLeaksMemory -XX:+UseZGC
+ */
+
import java.util.Arrays;
import java.util.ArrayList;
import java.util.regex.Pattern;
From e3320a9df592a06c466ae9158d8f173921679952 Mon Sep 17 00:00:00 2001
From: Nizar Benalla
Date: Mon, 6 Oct 2025 13:32:46 +0000
Subject: [PATCH 032/160] 8367610: Test
tools/sincechecker/modules/java.base/JavaBaseCheckSince.java timed out on
Windows
Reviewed-by: liach
---
.../sincechecker/modules/java.base/JavaBaseCheckSince.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/jdk/tools/sincechecker/modules/java.base/JavaBaseCheckSince.java b/test/jdk/tools/sincechecker/modules/java.base/JavaBaseCheckSince.java
index 64d5bf2465f..b75b6d9401a 100644
--- a/test/jdk/tools/sincechecker/modules/java.base/JavaBaseCheckSince.java
+++ b/test/jdk/tools/sincechecker/modules/java.base/JavaBaseCheckSince.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its 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
@@ -23,8 +23,8 @@
/*
* @test
- * @bug 8331051
+ * @bug 8331051 8367610
* @summary Test for `@since` in java.base module
* @library /test/lib /test/jdk/tools/sincechecker
- * @run main SinceChecker java.base --exclude java.lang.classfile
+ * @run main/timeout=480 SinceChecker java.base --exclude java.lang.classfile
*/
From b6a4cfecb731615b6ef70828ac10fae4b2264cdc Mon Sep 17 00:00:00 2001
From: Mahendra Chhipa
Date: Mon, 6 Oct 2025 15:26:59 +0000
Subject: [PATCH 033/160] 8367114: Update jdk.test.lib.net.SimpleHttpServer to
use SimpleFileServer
Reviewed-by: dfuchs, vyazici
---
.../catalog/CatalogFileInputTest.java | 27 +++-
.../sun/net/httpserver/SimpleFileServer.java | 71 --------
.../mrjar/MultiReleaseJarHttpProperties.java | 27 ++--
.../jar/MultiReleaseJarURLConnection.java | 27 ++--
.../jdk/test/lib/net/SimpleHttpServer.java | 152 ------------------
5 files changed, 53 insertions(+), 251 deletions(-)
delete mode 100644 test/jdk/com/sun/net/httpserver/SimpleFileServer.java
delete mode 100644 test/lib/jdk/test/lib/net/SimpleHttpServer.java
diff --git a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogFileInputTest.java b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogFileInputTest.java
index 66ddad86785..9a19c0237d7 100644
--- a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogFileInputTest.java
+++ b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogFileInputTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2025, Oracle and/or its 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
@@ -38,6 +38,8 @@ import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import javax.xml.catalog.Catalog;
import javax.xml.catalog.CatalogException;
@@ -49,6 +51,9 @@ import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.CREATE;
import static jaxp.library.JAXPTestUtilities.getSystemProperty;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.SimpleFileServer;
+import jdk.test.lib.net.URIBuilder;
import jdk.test.lib.util.JarUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
@@ -56,13 +61,11 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.xml.sax.InputSource;
-import jdk.test.lib.net.SimpleHttpServer;
/*
* @test
* @bug 8151154 8171243
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest /test/lib
- * @build jdk.test.lib.net.SimpleHttpServer
* @run testng/othervm catalog.CatalogFileInputTest
* @summary Verifies that the Catalog API accepts valid URIs only;
* Verifies that the CatalogFeatures' builder throws
@@ -81,9 +84,9 @@ public class CatalogFileInputTest extends CatalogSupportBase {
final static String SCHEME_JARFILE = "jar:";
static final String REMOTE_FILE_LOCATION = "/jar/META-INF";
static final String DOCROOT = SRC_DIR;
- static final String TESTCONTEXT = REMOTE_FILE_LOCATION; //mapped to local file path
- private SimpleHttpServer httpserver;
+ private HttpServer httpserver;
private String remoteFilePath;
+ private ExecutorService executor;
/*
* Initializing fields
@@ -92,15 +95,23 @@ public class CatalogFileInputTest extends CatalogSupportBase {
public void setUpClass() throws Exception {
super.setUp();
// set up HttpServer
- httpserver = new SimpleHttpServer(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), TESTCONTEXT, DOCROOT);
+ httpserver = SimpleFileServer.createFileServer(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0),
+ Path.of(DOCROOT), SimpleFileServer.OutputLevel.INFO);
+ executor = Executors.newCachedThreadPool();
+ httpserver.setExecutor(executor);
httpserver.start();
- remoteFilePath = httpserver.getAddress() + REMOTE_FILE_LOCATION;
+ remoteFilePath = URIBuilder.newBuilder()
+ .scheme("http")
+ .host(httpserver.getAddress().getAddress())
+ .port(httpserver.getAddress().getPort())
+ .build().toString() + REMOTE_FILE_LOCATION;
}
@AfterClass
protected void tearDown() {
if (httpserver != null) {
- httpserver.stop();
+ httpserver.stop(0);
+ executor.shutdown();
}
}
diff --git a/test/jdk/com/sun/net/httpserver/SimpleFileServer.java b/test/jdk/com/sun/net/httpserver/SimpleFileServer.java
deleted file mode 100644
index bff415c0b98..00000000000
--- a/test/jdk/com/sun/net/httpserver/SimpleFileServer.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2005, 2024, Oracle and/or its 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.
- *
- * 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.
- */
-
-import java.util.concurrent.*;
-import java.util.logging.*;
-import java.io.*;
-import java.net.*;
-
-import com.sun.net.httpserver.*;
-
-/**
- * Implements a basic static content HTTP server
- * which understands text/html, text/plain content types
- *
- * Must be given an abs pathname to the document root.
- * Directory listings together with text + html files
- * can be served.
- *
- * File Server created on files sub-path
- *
- * Echo server created on echo sub-path
- */
-public class SimpleFileServer {
-
- public static void main (String[] args) throws Exception {
- if (args.length != 3) {
- System.out.println ("usage: java FileServerHandler rootDir port logfilename");
- System.exit(1);
- }
- Logger logger = Logger.getLogger("com.sun.net.httpserver");
- ConsoleHandler ch = new ConsoleHandler();
- logger.setLevel(Level.ALL);
- ch.setLevel(Level.ALL);
- logger.addHandler(ch);
-
- String rootDir = args[0];
- int port = Integer.parseInt (args[1]);
- String logfile = args[2];
- HttpServer server = HttpServer.create (new InetSocketAddress (port), 0);
- HttpHandler h = new FileServerHandler (rootDir);
- HttpHandler h1 = new EchoHandler ();
-
- HttpContext c = server.createContext ("/files", h);
- c.getFilters().add (new LogFilter (new File (logfile)));
- HttpContext c1 = server.createContext ("/echo", h1);
- c.getFilters().add (new LogFilter (new File (logfile)));
- c1.getFilters().add (new LogFilter (new File (logfile)));
- server.setExecutor (Executors.newCachedThreadPool());
- server.start ();
- }
-}
diff --git a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarHttpProperties.java b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarHttpProperties.java
index 93cf0e0165d..16f764c6674 100644
--- a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarHttpProperties.java
+++ b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarHttpProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its 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
@@ -28,9 +28,7 @@
* @library /lib/testlibrary/java/util/jar /test/lib
* @modules jdk.jartool
* jdk.compiler
- * jdk.httpserver
* @build CreateMultiReleaseTestJars
- * jdk.test.lib.net.SimpleHttpServer
* jdk.test.lib.compiler.Compiler
* jdk.test.lib.util.JarBuilder
* @run testng MultiReleaseJarHttpProperties
@@ -51,21 +49,28 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URL;
import java.net.URLClassLoader;
+import java.nio.file.Path;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
-import jdk.test.lib.net.SimpleHttpServer;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.SimpleFileServer;
import jdk.test.lib.net.URIBuilder;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class MultiReleaseJarHttpProperties extends MultiReleaseJarProperties {
- private SimpleHttpServer server;
+ private HttpServer server;
+ private ExecutorService executor;
static final String TESTCONTEXT = "/multi-release.jar"; //mapped to local file path
@BeforeClass
public void initialize() throws Exception {
- server = new SimpleHttpServer(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), TESTCONTEXT,
- System.getProperty("user.dir", "."));
+ server = SimpleFileServer.createFileServer(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0),
+ Path.of(System.getProperty("user.dir", ".")), SimpleFileServer.OutputLevel.INFO);
+ executor = Executors.newCachedThreadPool();
+ server.setExecutor(executor);
server.start();
super.initialize();
}
@@ -73,7 +78,7 @@ public class MultiReleaseJarHttpProperties extends MultiReleaseJarProperties {
@Override
protected void initializeClassLoader() throws Exception {
URL[] urls = new URL[]{
- URIBuilder.newBuilder().scheme("http").port(server.getPort()).loopback()
+ URIBuilder.newBuilder().scheme("http").port(server.getAddress().getPort()).loopback()
.path(TESTCONTEXT).toURL(),
};
cldr = new URLClassLoader(urls);
@@ -84,8 +89,10 @@ public class MultiReleaseJarHttpProperties extends MultiReleaseJarProperties {
@AfterClass
public void close() throws IOException {
// Windows requires server to stop before file is deleted
- if (server != null)
- server.stop();
+ if (server != null) {
+ server.stop(0);
+ executor.shutdown();
+ }
super.close();
}
diff --git a/test/jdk/sun/net/www/protocol/jar/MultiReleaseJarURLConnection.java b/test/jdk/sun/net/www/protocol/jar/MultiReleaseJarURLConnection.java
index 2d5ba81194f..f113e7d3fcd 100644
--- a/test/jdk/sun/net/www/protocol/jar/MultiReleaseJarURLConnection.java
+++ b/test/jdk/sun/net/www/protocol/jar/MultiReleaseJarURLConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its 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
@@ -27,10 +27,8 @@
* @summary Test that URL connections to multi-release jars can be runtime versioned
* @library /lib/testlibrary/java/util/jar /test/lib
* @modules jdk.compiler
- * jdk.httpserver
* jdk.jartool
* @build CreateMultiReleaseTestJars
- * jdk.test.lib.net.SimpleHttpServer
* jdk.test.lib.util.JarBuilder
* jdk.test.lib.compiler.Compiler
* @run testng MultiReleaseJarURLConnection
@@ -51,11 +49,15 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.jar.JarFile;
-import jdk.test.lib.net.SimpleHttpServer;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.SimpleFileServer;
import jdk.test.lib.net.URIBuilder;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
@@ -68,8 +70,8 @@ public class MultiReleaseJarURLConnection {
String unversioned = userdir + "/unversioned.jar";
String unsigned = userdir + "/multi-release.jar";
String signed = userdir + "/signed-multi-release.jar";
- static final String TESTCONTEXT = "/multi-release.jar";
- SimpleHttpServer server;
+ HttpServer server;
+ ExecutorService executor;
@BeforeClass
public void initialize() throws Exception {
@@ -78,7 +80,10 @@ public class MultiReleaseJarURLConnection {
creator.buildUnversionedJar();
creator.buildMultiReleaseJar();
creator.buildSignedMultiReleaseJar();
- server = new SimpleHttpServer(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), TESTCONTEXT, System.getProperty("user.dir", "."));
+ server = SimpleFileServer.createFileServer(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0),
+ Path.of(System.getProperty("user.dir", ".")), SimpleFileServer.OutputLevel.INFO);
+ executor = Executors.newCachedThreadPool();
+ server.setExecutor(executor);
server.start();
}
@@ -86,7 +91,9 @@ public class MultiReleaseJarURLConnection {
public void close() throws IOException {
// Windows requires server to stop before file is deleted
if (server != null)
- server.stop();
+ server.stop(0);
+ executor.shutdown();
+
Files.delete(Paths.get(unversioned));
Files.delete(Paths.get(unsigned));
Files.delete(Paths.get(signed));
@@ -176,8 +183,8 @@ public class MultiReleaseJarURLConnection {
{"unsigned", new URL("jar:file:" + unsigned + "!/")},
{"signed", new URL("jar:file:" + signed + "!/")},
// external jar received via http protocol
- {"http", toHttpJarURL(server.getPort(), "/multi-release.jar", "!/")},
- {"http", URIBuilder.newBuilder().scheme("http").port(server.getPort())
+ {"http", toHttpJarURL(server.getAddress().getPort(), "/multi-release.jar", "!/")},
+ {"http", URIBuilder.newBuilder().scheme("http").port(server.getAddress().getPort())
.loopback().path("/multi-release.jar").toURL()},
};
}
diff --git a/test/lib/jdk/test/lib/net/SimpleHttpServer.java b/test/lib/jdk/test/lib/net/SimpleHttpServer.java
deleted file mode 100644
index 1905091eac6..00000000000
--- a/test/lib/jdk/test/lib/net/SimpleHttpServer.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (c) 2017, 2024, Oracle and/or its 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.
- *
- * 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 jdk.test.lib.net;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.file.FileSystemNotFoundException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import com.sun.net.httpserver.Headers;
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpServer;
-
-/**
- * A simple HTTP Server.
- **/
-public class SimpleHttpServer {
- private final HttpServer httpServer;
- private ExecutorService executor;
- private String address;
- private final String context;
- private final String docRoot;
- private final InetSocketAddress inetSocketAddress;
-
- public SimpleHttpServer(final InetSocketAddress inetSocketAddress, final String context, final String docRoot)
- throws IOException {
- this.inetSocketAddress = inetSocketAddress;
- this.context = context;
- this.docRoot = docRoot;
- httpServer = HttpServer.create();
- }
-
- public void start() throws IOException, URISyntaxException {
- MyHttpHandler handler = new MyHttpHandler(docRoot);
- httpServer.bind(inetSocketAddress, 0);
- httpServer.createContext(context, handler);
- executor = Executors.newCachedThreadPool();
- httpServer.setExecutor(executor);
- httpServer.start();
- address = "http:" + URIBuilder.newBuilder().host(httpServer.getAddress().getAddress()).
- port(httpServer.getAddress().getPort()).build().toString();
- }
-
- public void stop() {
- httpServer.stop(0);
- executor.shutdown();
- }
-
- public String getAddress() {
- return address;
- }
-
- public int getPort() {
- return httpServer.getAddress().getPort();
- }
-
- class MyHttpHandler implements HttpHandler {
- private final URI rootUri;
-
- MyHttpHandler(final String docroot) {
- rootUri = Path.of(docroot).toUri().normalize();
- }
-
- public void handle(final HttpExchange t) throws IOException {
- try (InputStream is = t.getRequestBody()) {
- is.readAllBytes();
- Headers rMap = t.getResponseHeaders();
- try (OutputStream os = t.getResponseBody()) {
- URI uri = t.getRequestURI();
- String path = uri.getRawPath();
- assert path.isEmpty() || path.startsWith("/");
- Path fPath;
- try {
- uri = URI.create("file://" + rootUri.getRawPath() + path).normalize();
- fPath = Path.of(uri);
- } catch (IllegalArgumentException | FileSystemNotFoundException ex) {
- ex.printStackTrace();
- notfound(t, path);
- return;
- }
- byte[] bytes = Files.readAllBytes(fPath);
- String method = t.getRequestMethod();
- if (method.equals("HEAD")) {
- rMap.set("Content-Length", Long.toString(bytes.length));
- t.sendResponseHeaders(200, -1);
- t.close();
- } else if (!method.equals("GET")) {
- t.sendResponseHeaders(405, -1);
- t.close();
- return;
- }
- if (path.endsWith(".html") || path.endsWith(".htm")) {
- rMap.set("Content-Type", "text/html");
- } else {
- rMap.set("Content-Type", "text/plain");
- }
- t.sendResponseHeaders(200, bytes.length);
- os.write(bytes);
- }
- }
- }
- void moved(final HttpExchange t) throws IOException {
- Headers req = t.getRequestHeaders();
- Headers map = t.getResponseHeaders();
- URI uri = t.getRequestURI();
- String host = req.getFirst("Host");
- String location = "http://" + host + uri.getPath() + "/";
- map.set("Content-Type", "text/html");
- map.set("Location", location);
- t.sendResponseHeaders(301, -1);
- t.close();
- }
- void notfound(final HttpExchange t, final String p) throws IOException {
- t.getResponseHeaders().set("Content-Type", "text/html");
- t.sendResponseHeaders(404, 0);
- try (OutputStream os = t.getResponseBody()) {
- String s = "File not found
";
- s = s + p + "";
- os.write(s.getBytes());
- }
- t.close();
- }
- }
-}
From 596af0a7cc37e359d54689be20f855a86ae46567 Mon Sep 17 00:00:00 2001
From: Albert Mingkun Yang
Date: Mon, 6 Oct 2025 15:44:13 +0000
Subject: [PATCH 034/160] 8369041: Release memory after testing in
ThreadsRunner.java
Reviewed-by: shade, tschatzl
---
.../jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java b/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java
index 0401fea13fc..39e7e2c2237 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java
@@ -310,6 +310,10 @@ public class ThreadsRunner implements MultiRunner, LogAware, RunParamsAware {
log.info("Unexpected exception during the run.");
log.info(t);
successful = false;
+ } finally {
+ // Finished testing; release memory to avoid OOM.
+ runnables.clear();
+ threads.clear();
}
}
From 0f406c420e35f7a4358dc99711fd23d162f21777 Mon Sep 17 00:00:00 2001
From: Justin Lu
Date: Mon, 6 Oct 2025 16:11:59 +0000
Subject: [PATCH 035/160] 8369078: Fix faulty test conversion in
IllegalCharsetName.java
Reviewed-by: naoto, alanb
---
test/jdk/java/nio/charset/Charset/IllegalCharsetName.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/jdk/java/nio/charset/Charset/IllegalCharsetName.java b/test/jdk/java/nio/charset/Charset/IllegalCharsetName.java
index 266801cf52b..9f2879a3ff0 100644
--- a/test/jdk/java/nio/charset/Charset/IllegalCharsetName.java
+++ b/test/jdk/java/nio/charset/Charset/IllegalCharsetName.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, Oracle and/or its 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
@@ -50,7 +50,7 @@ public class IllegalCharsetName {
assertThrows(IllegalCharsetNameException.class,
() -> Charset.forName(name));
assertThrows(IllegalCharsetNameException.class,
- () -> Charset.forName(name));
+ () -> Charset.isSupported(name));
}
// Charset.forName, Charset.isSupported, and the Charset constructor should
@@ -60,7 +60,7 @@ public class IllegalCharsetName {
assertThrows(IllegalCharsetNameException.class,
() -> Charset.forName(""));
assertThrows(IllegalCharsetNameException.class,
- () -> Charset.forName(""));
+ () -> Charset.isSupported(""));
assertThrows(IllegalCharsetNameException.class,
() -> new Charset("", new String[]{}) {
@Override
From 2376a9e9727e9cb3020dd3f57584950a4cdcdab6 Mon Sep 17 00:00:00 2001
From: Erik Gahlin
Date: Mon, 6 Oct 2025 17:30:42 +0000
Subject: [PATCH 036/160] 8365630: jdk/jfr/tool/TestPrintContextual.java fails
with wrong spanId
Reviewed-by: shade
---
test/jdk/jdk/jfr/tool/TestPrintContextual.java | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/test/jdk/jdk/jfr/tool/TestPrintContextual.java b/test/jdk/jdk/jfr/tool/TestPrintContextual.java
index 15486148b9c..e6df9c7c729 100644
--- a/test/jdk/jdk/jfr/tool/TestPrintContextual.java
+++ b/test/jdk/jdk/jfr/tool/TestPrintContextual.java
@@ -25,6 +25,7 @@ package jdk.jfr.tool;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.time.Instant;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
@@ -264,7 +265,8 @@ public class TestPrintContextual {
}
}
- private static void span(int depth) {
+ private static void span(int depth) throws InterruptedException {
+ awaitUniqueTimestamp();
SpanEvent span = new SpanEvent();
span.name = "span";
span.spanId = depth;
@@ -277,6 +279,13 @@ public class TestPrintContextual {
span.commit();
}
+ private static void awaitUniqueTimestamp() throws InterruptedException {
+ Instant timestamp = Instant.now();
+ while (timestamp.equals(Instant.now())) {
+ Thread.sleep(1);
+ }
+ }
+
// Tests that context values are only inhjected into events in the same thread.
private static void testThreadedContext() throws Exception {
try (Recording r = new Recording()) {
From eb34a117934951af075a425ce2cf8d3b1ced9700 Mon Sep 17 00:00:00 2001
From: Mikael Vidstedt
Date: Tue, 7 Oct 2025 00:52:38 +0000
Subject: [PATCH 037/160] 8369242: Rename URL variables in devkit/Tools.gmk
Reviewed-by: erikj
---
make/devkit/Tools.gmk | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk
index f27d47b822c..6241674071c 100644
--- a/make/devkit/Tools.gmk
+++ b/make/devkit/Tools.gmk
@@ -117,13 +117,13 @@ dependencies := gcc binutils ccache mpfr gmp mpc gdb
$(foreach dep,$(dependencies),$(eval $(dep)_ver := $(dep)-$($(dep)_ver_only)))
-GCC := http://ftp.gnu.org/pub/gnu/gcc/$(gcc_ver)/$(gcc_ver).tar.xz
-BINUTILS := http://ftp.gnu.org/pub/gnu/binutils/$(binutils_ver).tar.gz
-CCACHE := https://github.com/ccache/ccache/releases/download/v$(ccache_ver_only)/$(ccache_ver).tar.xz
-MPFR := https://www.mpfr.org/$(mpfr_ver)/$(mpfr_ver).tar.bz2
-GMP := http://ftp.gnu.org/pub/gnu/gmp/$(gmp_ver).tar.bz2
-MPC := http://ftp.gnu.org/pub/gnu/mpc/$(mpc_ver).tar.gz
-GDB := http://ftp.gnu.org/gnu/gdb/$(gdb_ver).tar.xz
+GCC_URL := http://ftp.gnu.org/pub/gnu/gcc/$(gcc_ver)/$(gcc_ver).tar.xz
+BINUTILS_URL := http://ftp.gnu.org/pub/gnu/binutils/$(binutils_ver).tar.gz
+CCACHE_URL := https://github.com/ccache/ccache/releases/download/v$(ccache_ver_only)/$(ccache_ver).tar.xz
+MPFR_URL := https://www.mpfr.org/$(mpfr_ver)/$(mpfr_ver).tar.bz2
+GMP_URL := http://ftp.gnu.org/pub/gnu/gmp/$(gmp_ver).tar.bz2
+MPC_URL := http://ftp.gnu.org/pub/gnu/mpc/$(mpc_ver).tar.gz
+GDB_URL := http://ftp.gnu.org/gnu/gdb/$(gdb_ver).tar.xz
REQUIRED_MIN_MAKE_MAJOR_VERSION := 4
ifneq ($(REQUIRED_MIN_MAKE_MAJOR_VERSION),)
@@ -201,7 +201,7 @@ download-rpms:
# Generate downloading + unpacking of sources.
define Download
# Allow override
- $(1)_DIRNAME ?= $(basename $(basename $(notdir $($(1)))))
+ $(1)_DIRNAME ?= $(basename $(basename $(notdir $($(1)_URL))))
$(1)_DIR = $(abspath $(SRCDIR)/$$($(1)_DIRNAME))
ifeq ($$($(1)_CMAKE_BASED),)
$(1)_CFG = $$($(1)_DIR)/configure
@@ -212,7 +212,7 @@ define Download
$(1)_SRC_MARKER = $$($(1)_DIR)/CMakeLists.txt
$(1)_CONFIG = $$(CMAKE_CONFIG) $$($(1)_DIR)
endif
- $(1)_FILE = $(DOWNLOAD)/$(notdir $($(1)))
+ $(1)_FILE = $(DOWNLOAD)/$(notdir $($(1)_URL))
$$($(1)_SRC_MARKER) : $$($(1)_FILE)
mkdir -p $$(SRCDIR)
@@ -224,7 +224,7 @@ define Download
touch $$@
$$($(1)_FILE) :
- wget -P $(DOWNLOAD) $$($(1))
+ wget -P $(DOWNLOAD) $$($(1)_URL)
endef
# Download and unpack all source packages
From e783c524c17e1d1a3fff4b6370e222134e66edc8 Mon Sep 17 00:00:00 2001
From: Prasanta Sadhukhan
Date: Tue, 7 Oct 2025 04:08:32 +0000
Subject: [PATCH 038/160] 8368185: Test
javax/swing/plaf/synth/SynthButtonUI/6276188/bug6276188.java failed: Synth
ButtonUI does not handle PRESSED & MOUSE_OVER state
Reviewed-by: tr, aivanov
---
.../SynthButtonUI/6276188/bug6276188.java | 51 +++++++++++++------
1 file changed, 36 insertions(+), 15 deletions(-)
diff --git a/test/jdk/javax/swing/plaf/synth/SynthButtonUI/6276188/bug6276188.java b/test/jdk/javax/swing/plaf/synth/SynthButtonUI/6276188/bug6276188.java
index be9c9457fff..9b689d2fe2c 100644
--- a/test/jdk/javax/swing/plaf/synth/SynthButtonUI/6276188/bug6276188.java
+++ b/test/jdk/javax/swing/plaf/synth/SynthButtonUI/6276188/bug6276188.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025, Oracle and/or its 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
@@ -21,40 +21,53 @@
* questions.
*/
/**
- * @test 1.4 08/08/05
+ * @test
* @key headful
* @bug 6276188
* @library ../../../../regtesthelpers
* @build Util
- * @author Romain Guy
* @summary Tests PRESSED and MOUSE_OVER and FOCUSED state for buttons with Synth.
* @run main/othervm -Dsun.java2d.uiScale=1 bug6276188
*/
-import java.awt.*;
-import java.awt.image.*;
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.plaf.synth.*;
+import java.io.File;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.Toolkit;
+import java.awt.image.BufferedImage;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import javax.imageio.ImageIO;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.plaf.synth.SynthLookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import java.util.concurrent.CountDownLatch;
public class bug6276188 {
private static JButton button;
- private static Point p;
private static JFrame testFrame;
// move away from cursor
- private final static int OFFSET_X = -20;
- private final static int OFFSET_Y = -20;
+ private final static int OFFSET_X = 20;
+ private final static int OFFSET_Y = 20;
public static void main(String[] args) throws Throwable {
+ Robot robot = new Robot();
try {
- Robot robot = new Robot();
robot.setAutoDelay(100);
SynthLookAndFeel lookAndFeel = new SynthLookAndFeel();
lookAndFeel.load(bug6276188.class.getResourceAsStream("bug6276188.xml"), bug6276188.class);
UIManager.setLookAndFeel(lookAndFeel);
+ CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
@@ -62,6 +75,13 @@ public class bug6276188 {
testFrame.setLayout(new BorderLayout());
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
testFrame.add(BorderLayout.CENTER, button = new JButton());
+ button.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent e) {
+ System.out.println("Mouse pressed");
+ latch.countDown();
+ }
+ });
testFrame.setSize(new Dimension(320, 200));
testFrame.setLocationRelativeTo(null);
@@ -72,13 +92,14 @@ public class bug6276188 {
robot.waitForIdle();
robot.delay(1000);
- p = Util.getCenterPoint(button);
+ Point p = Util.getCenterPoint(button);
System.out.println("Button center point: " + p);
robot.mouseMove(p.x , p.y);
robot.waitForIdle();
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
- robot.waitForIdle();
+ latch.await();
+ robot.delay(1000);
Color color = robot.getPixelColor(p.x - OFFSET_X, p.y - OFFSET_Y);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
@@ -89,7 +110,7 @@ public class bug6276188 {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle screen = new Rectangle(0, 0, (int) screenSize.getWidth(), (int) screenSize.getHeight());
BufferedImage img = robot.createScreenCapture(screen);
- javax.imageio.ImageIO.write(img, "png", new java.io.File("image.png"));
+ ImageIO.write(img, "png", new File("image.png"));
throw new RuntimeException("Synth ButtonUI does not handle PRESSED & MOUSE_OVER state");
}
} finally {
From 07549f3e1539a2dd491a4f9ffe9df8580d7d7dea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Maillard?=
Date: Tue, 7 Oct 2025 07:43:43 +0000
Subject: [PATCH 039/160] 8360389: Support printing from C2 compiled code
Reviewed-by: kvn, thartmann, mhaessig
---
src/hotspot/share/opto/compile.cpp | 155 ++++++++++++++++++++
src/hotspot/share/opto/compile.hpp | 22 +++
src/hotspot/share/opto/runtime.cpp | 56 +++++++
src/hotspot/share/opto/runtime.hpp | 10 ++
src/hotspot/share/runtime/sharedRuntime.cpp | 40 +++++
src/hotspot/share/runtime/sharedRuntime.hpp | 34 +++++
6 files changed, 317 insertions(+)
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index 7f63efe9e5e..6babc13e1b3 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -5378,3 +5378,158 @@ Node* Compile::narrow_value(BasicType bt, Node* value, const Type* type, PhaseGV
void Compile::record_method_not_compilable_oom() {
record_method_not_compilable(CompilationMemoryStatistic::failure_reason_memlimit());
}
+
+#ifndef PRODUCT
+// Collects all the control inputs from nodes on the worklist and from their data dependencies
+static void find_candidate_control_inputs(Unique_Node_List& worklist, Unique_Node_List& candidates) {
+ // Follow non-control edges until we reach CFG nodes
+ for (uint i = 0; i < worklist.size(); i++) {
+ const Node* n = worklist.at(i);
+ for (uint j = 0; j < n->req(); j++) {
+ Node* in = n->in(j);
+ if (in == nullptr || in->is_Root()) {
+ continue;
+ }
+ if (in->is_CFG()) {
+ if (in->is_Call()) {
+ // The return value of a call is only available if the call did not result in an exception
+ Node* control_proj_use = in->as_Call()->proj_out(TypeFunc::Control)->unique_out();
+ if (control_proj_use->is_Catch()) {
+ Node* fall_through = control_proj_use->as_Catch()->proj_out(CatchProjNode::fall_through_index);
+ candidates.push(fall_through);
+ continue;
+ }
+ }
+
+ if (in->is_Multi()) {
+ // We got here by following data inputs so we should only have one control use
+ // (no IfNode, etc)
+ assert(!n->is_MultiBranch(), "unexpected node type: %s", n->Name());
+ candidates.push(in->as_Multi()->proj_out(TypeFunc::Control));
+ } else {
+ candidates.push(in);
+ }
+ } else {
+ worklist.push(in);
+ }
+ }
+ }
+}
+
+// Returns the candidate node that is a descendant to all the other candidates
+static Node* pick_control(Unique_Node_List& candidates) {
+ Unique_Node_List worklist;
+ worklist.copy(candidates);
+
+ // Traverse backwards through the CFG
+ for (uint i = 0; i < worklist.size(); i++) {
+ const Node* n = worklist.at(i);
+ if (n->is_Root()) {
+ continue;
+ }
+ for (uint j = 0; j < n->req(); j++) {
+ // Skip backedge of loops to avoid cycles
+ if (n->is_Loop() && j == LoopNode::LoopBackControl) {
+ continue;
+ }
+
+ Node* pred = n->in(j);
+ if (pred != nullptr && pred != n && pred->is_CFG()) {
+ worklist.push(pred);
+ // if pred is an ancestor of n, then pred is an ancestor to at least one candidate
+ candidates.remove(pred);
+ }
+ }
+ }
+
+ assert(candidates.size() == 1, "unexpected control flow");
+ return candidates.at(0);
+}
+
+// Initialize a parameter input for a debug print call, using a placeholder for jlong and jdouble
+static void debug_print_init_parm(Node* call, Node* parm, Node* half, int* pos) {
+ call->init_req((*pos)++, parm);
+ const BasicType bt = parm->bottom_type()->basic_type();
+ if (bt == T_LONG || bt == T_DOUBLE) {
+ call->init_req((*pos)++, half);
+ }
+}
+
+Node* Compile::make_debug_print_call(const char* str, address call_addr, PhaseGVN* gvn,
+ Node* parm0, Node* parm1,
+ Node* parm2, Node* parm3,
+ Node* parm4, Node* parm5,
+ Node* parm6) const {
+ Node* str_node = gvn->transform(new ConPNode(TypeRawPtr::make(((address) str))));
+ const TypeFunc* type = OptoRuntime::debug_print_Type(parm0, parm1, parm2, parm3, parm4, parm5, parm6);
+ Node* call = new CallLeafNode(type, call_addr, "debug_print", TypeRawPtr::BOTTOM);
+
+ // find the most suitable control input
+ Unique_Node_List worklist, candidates;
+ if (parm0 != nullptr) { worklist.push(parm0);
+ if (parm1 != nullptr) { worklist.push(parm1);
+ if (parm2 != nullptr) { worklist.push(parm2);
+ if (parm3 != nullptr) { worklist.push(parm3);
+ if (parm4 != nullptr) { worklist.push(parm4);
+ if (parm5 != nullptr) { worklist.push(parm5);
+ if (parm6 != nullptr) { worklist.push(parm6);
+ /* close each nested if ===> */ } } } } } } }
+ find_candidate_control_inputs(worklist, candidates);
+ Node* control = nullptr;
+ if (candidates.size() == 0) {
+ control = C->start()->proj_out(TypeFunc::Control);
+ } else {
+ control = pick_control(candidates);
+ }
+
+ // find all the previous users of the control we picked
+ GrowableArray users_of_control;
+ for (DUIterator_Fast kmax, i = control->fast_outs(kmax); i < kmax; i++) {
+ Node* use = control->fast_out(i);
+ if (use->is_CFG() && use != control) {
+ users_of_control.push(use);
+ }
+ }
+
+ // we do not actually care about IO and memory as it uses neither
+ call->init_req(TypeFunc::Control, control);
+ call->init_req(TypeFunc::I_O, top());
+ call->init_req(TypeFunc::Memory, top());
+ call->init_req(TypeFunc::FramePtr, C->start()->proj_out(TypeFunc::FramePtr));
+ call->init_req(TypeFunc::ReturnAdr, top());
+
+ int pos = TypeFunc::Parms;
+ call->init_req(pos++, str_node);
+ if (parm0 != nullptr) { debug_print_init_parm(call, parm0, top(), &pos);
+ if (parm1 != nullptr) { debug_print_init_parm(call, parm1, top(), &pos);
+ if (parm2 != nullptr) { debug_print_init_parm(call, parm2, top(), &pos);
+ if (parm3 != nullptr) { debug_print_init_parm(call, parm3, top(), &pos);
+ if (parm4 != nullptr) { debug_print_init_parm(call, parm4, top(), &pos);
+ if (parm5 != nullptr) { debug_print_init_parm(call, parm5, top(), &pos);
+ if (parm6 != nullptr) { debug_print_init_parm(call, parm6, top(), &pos);
+ /* close each nested if ===> */ } } } } } } }
+ assert(call->in(call->req()-1) != nullptr, "must initialize all parms");
+
+ call = gvn->transform(call);
+ Node* call_control_proj = gvn->transform(new ProjNode(call, TypeFunc::Control));
+
+ // rewire previous users to have the new call as control instead
+ PhaseIterGVN* igvn = gvn->is_IterGVN();
+ for (int i = 0; i < users_of_control.length(); i++) {
+ Node* use = users_of_control.at(i);
+ for (uint j = 0; j < use->req(); j++) {
+ if (use->in(j) == control) {
+ if (igvn != nullptr) {
+ igvn->replace_input_of(use, j, call_control_proj);
+ } else {
+ gvn->hash_delete(use);
+ use->set_req(j, call_control_proj);
+ gvn->hash_insert(use);
+ }
+ }
+ }
+ }
+
+ return call;
+}
+#endif // !PRODUCT
diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp
index a68da644d82..66a5497a7ad 100644
--- a/src/hotspot/share/opto/compile.hpp
+++ b/src/hotspot/share/opto/compile.hpp
@@ -1316,6 +1316,28 @@ public:
BasicType out_bt, BasicType in_bt);
static Node* narrow_value(BasicType bt, Node* value, const Type* type, PhaseGVN* phase, bool transform_res);
+
+#ifndef PRODUCT
+private:
+ // getting rid of the template makes things easier
+ Node* make_debug_print_call(const char* str, address call_addr, PhaseGVN* gvn,
+ Node* parm0 = nullptr, Node* parm1 = nullptr,
+ Node* parm2 = nullptr, Node* parm3 = nullptr,
+ Node* parm4 = nullptr, Node* parm5 = nullptr,
+ Node* parm6 = nullptr) const;
+
+public:
+ // Creates a CallLeafNode for a runtime call that prints a static string and the values of the
+ // nodes passed as arguments.
+ // This function also takes care of doing the necessary wiring, including finding a suitable control
+ // based on the nodes that need to be printed. Note that passing nodes that have incompatible controls
+ // is undefined behavior.
+ template
+ Node* make_debug_print(const char* str, PhaseGVN* gvn, NN... in) {
+ address call_addr = CAST_FROM_FN_PTR(address, SharedRuntime::debug_print);
+ return make_debug_print_call(str, call_addr, gvn, in...);
+ }
+#endif
};
#endif // SHARE_OPTO_COMPILE_HPP
diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp
index 072b1384921..9de9fe5da03 100644
--- a/src/hotspot/share/opto/runtime.cpp
+++ b/src/hotspot/share/opto/runtime.cpp
@@ -1780,6 +1780,62 @@ static const TypeFunc* make_osr_end_Type() {
return TypeFunc::make(domain, range);
}
+#ifndef PRODUCT
+static void debug_print_convert_type(const Type** fields, int* argp, Node *parm) {
+ const BasicType bt = parm->bottom_type()->basic_type();
+ fields[(*argp)++] = Type::get_const_basic_type(bt);
+ if (bt == T_LONG || bt == T_DOUBLE) {
+ fields[(*argp)++] = Type::HALF;
+ }
+}
+
+static void update_arg_cnt(const Node* parm, int* arg_cnt) {
+ (*arg_cnt)++;
+ const BasicType bt = parm->bottom_type()->basic_type();
+ if (bt == T_LONG || bt == T_DOUBLE) {
+ (*arg_cnt)++;
+ }
+}
+
+const TypeFunc* OptoRuntime::debug_print_Type(Node* parm0, Node* parm1,
+ Node* parm2, Node* parm3,
+ Node* parm4, Node* parm5,
+ Node* parm6) {
+ int argcnt = 1;
+ if (parm0 != nullptr) { update_arg_cnt(parm0, &argcnt);
+ if (parm1 != nullptr) { update_arg_cnt(parm1, &argcnt);
+ if (parm2 != nullptr) { update_arg_cnt(parm2, &argcnt);
+ if (parm3 != nullptr) { update_arg_cnt(parm3, &argcnt);
+ if (parm4 != nullptr) { update_arg_cnt(parm4, &argcnt);
+ if (parm5 != nullptr) { update_arg_cnt(parm5, &argcnt);
+ if (parm6 != nullptr) { update_arg_cnt(parm6, &argcnt);
+ /* close each nested if ===> */ } } } } } } }
+
+ // create input type (domain)
+ const Type** fields = TypeTuple::fields(argcnt);
+ int argp = TypeFunc::Parms;
+ fields[argp++] = TypePtr::NOTNULL; // static string pointer
+
+ if (parm0 != nullptr) { debug_print_convert_type(fields, &argp, parm0);
+ if (parm1 != nullptr) { debug_print_convert_type(fields, &argp, parm1);
+ if (parm2 != nullptr) { debug_print_convert_type(fields, &argp, parm2);
+ if (parm3 != nullptr) { debug_print_convert_type(fields, &argp, parm3);
+ if (parm4 != nullptr) { debug_print_convert_type(fields, &argp, parm4);
+ if (parm5 != nullptr) { debug_print_convert_type(fields, &argp, parm5);
+ if (parm6 != nullptr) { debug_print_convert_type(fields, &argp, parm6);
+ /* close each nested if ===> */ } } } } } } }
+
+ assert(argp == TypeFunc::Parms+argcnt, "correct decoding");
+ const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields);
+
+ // no result type needed
+ fields = TypeTuple::fields(1);
+ fields[TypeFunc::Parms+0] = nullptr; // void
+ const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields);
+ return TypeFunc::make(domain, range);
+}
+#endif // PRODUCT
+
//-------------------------------------------------------------------------------------
// register policy
diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp
index 40e436c0d5c..76e69fd9d36 100644
--- a/src/hotspot/share/opto/runtime.hpp
+++ b/src/hotspot/share/opto/runtime.hpp
@@ -737,6 +737,16 @@ private:
return _dtrace_object_alloc_Type;
}
+#ifndef PRODUCT
+ // Signature for runtime calls in debug printing nodes, which depends on which nodes are actually passed
+ // Note: we do not allow more than 7 node arguments as GraphKit::make_runtime_call only allows 8, and we need
+ // one for the static string
+ static const TypeFunc* debug_print_Type(Node* parm0 = nullptr, Node* parm1 = nullptr,
+ Node* parm2 = nullptr, Node* parm3 = nullptr,
+ Node* parm4 = nullptr, Node* parm5 = nullptr,
+ Node* parm6 = nullptr);
+#endif // PRODUCT
+
private:
static NamedCounter * volatile _named_counters;
diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
index 85aeba361ff..a8c9a64d63a 100644
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -264,6 +264,46 @@ void SharedRuntime::print_ic_miss_histogram() {
tty->print_cr("Total IC misses: %7d", tot_misses);
}
}
+
+#ifdef COMPILER2
+// Runtime methods for printf-style debug nodes (same printing format as fieldDescriptor::print_on_for)
+void SharedRuntime::debug_print_value(jboolean x) {
+ tty->print_cr("boolean %d", x);
+}
+
+void SharedRuntime::debug_print_value(jbyte x) {
+ tty->print_cr("byte %d", x);
+}
+
+void SharedRuntime::debug_print_value(jshort x) {
+ tty->print_cr("short %d", x);
+}
+
+void SharedRuntime::debug_print_value(jchar x) {
+ tty->print_cr("char %c %d", isprint(x) ? x : ' ', x);
+}
+
+void SharedRuntime::debug_print_value(jint x) {
+ tty->print_cr("int %d", x);
+}
+
+void SharedRuntime::debug_print_value(jlong x) {
+ tty->print_cr("long " JLONG_FORMAT, x);
+}
+
+void SharedRuntime::debug_print_value(jfloat x) {
+ tty->print_cr("float %f", x);
+}
+
+void SharedRuntime::debug_print_value(jdouble x) {
+ tty->print_cr("double %lf", x);
+}
+
+void SharedRuntime::debug_print_value(oopDesc* x) {
+ x->print();
+}
+#endif // COMPILER2
+
#endif // PRODUCT
diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp
index 2a19b80c3b5..93cd92b3a32 100644
--- a/src/hotspot/share/runtime/sharedRuntime.hpp
+++ b/src/hotspot/share/runtime/sharedRuntime.hpp
@@ -32,6 +32,7 @@
#include "memory/allStatic.hpp"
#include "memory/metaspaceClosure.hpp"
#include "memory/resourceArea.hpp"
+#include "runtime/safepointVerifiers.hpp"
#include "runtime/stubInfo.hpp"
#include "utilities/macros.hpp"
@@ -635,6 +636,39 @@ class SharedRuntime: AllStatic {
static void print_call_statistics(uint64_t comp_total);
static void print_ic_miss_histogram();
+#ifdef COMPILER2
+ // Runtime methods for printf-style debug nodes
+ static void debug_print_value(jboolean x);
+ static void debug_print_value(jbyte x);
+ static void debug_print_value(jshort x);
+ static void debug_print_value(jchar x);
+ static void debug_print_value(jint x);
+ static void debug_print_value(jlong x);
+ static void debug_print_value(jfloat x);
+ static void debug_print_value(jdouble x);
+ static void debug_print_value(oopDesc* x);
+
+ template
+ static void debug_print_rec(T arg, Rest... args) {
+ debug_print_value(arg);
+ debug_print_rec(args...);
+ }
+
+ static void debug_print_rec() {}
+
+ // template is required here as we need to know the exact signature at compile-time
+ template
+ static void debug_print(const char *str, TT... args) {
+ // these three lines are the manual expansion of JRT_LEAF ... JRT_END, does not work well with templates
+ DEBUG_ONLY(NoHandleMark __hm;)
+ os::verify_stack_alignment();
+ DEBUG_ONLY(NoSafepointVerifier __nsv;)
+
+ tty->print_cr("%s", str);
+ debug_print_rec(args...);
+ }
+#endif // COMPILER2
+
#endif // PRODUCT
static void print_statistics() PRODUCT_RETURN;
From c06d6805aae3af2e6175f3f43deea46c9ce08bc6 Mon Sep 17 00:00:00 2001
From: Daniel Skantz
Date: Tue, 7 Oct 2025 09:04:39 +0000
Subject: [PATCH 040/160] 8362394: C2: Repeated stacked string concatenation
fails with "Hit MemLimit" and other resourcing errors
Reviewed-by: chagedorn, rcastanedalo
---
src/hotspot/share/opto/stringopts.cpp | 22 ++++-
.../stringopts/TestStackedConcatsMany.java | 95 +++++++++++++++++++
2 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsMany.java
diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp
index 25aa82870c3..420423dd246 100644
--- a/src/hotspot/share/opto/stringopts.cpp
+++ b/src/hotspot/share/opto/stringopts.cpp
@@ -53,6 +53,11 @@ class StringConcat : public ResourceObj {
Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten
// to restart at the initial JVMState.
+ static constexpr uint STACKED_CONCAT_UPPER_BOUND = 256; // argument limit for a merged concat.
+ // The value 256 was derived by measuring
+ // compilation time on variable length sequences
+ // of stackable concatenations and chosen to keep
+ // a safe margin to any critical point.
public:
// Mode for converting arguments to Strings
enum {
@@ -295,6 +300,8 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) {
}
assert(result->_control.contains(other->_end), "what?");
assert(result->_control.contains(_begin), "what?");
+
+ uint arguments_appended = 0;
for (int x = 0; x < num_arguments(); x++) {
Node* argx = argument_uncast(x);
if (argx == arg) {
@@ -303,8 +310,21 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) {
for (int y = 0; y < other->num_arguments(); y++) {
result->append(other->argument(y), other->mode(y));
}
+ arguments_appended += other->num_arguments();
} else {
result->append(argx, mode(x));
+ arguments_appended++;
+ }
+ // Check if this concatenation would result in an excessive number of arguments
+ // -- leading to high memory use, compilation time, and later, a large number of IR nodes
+ // -- and bail out in that case.
+ if (arguments_appended > STACKED_CONCAT_UPPER_BOUND) {
+#ifndef PRODUCT
+ if (PrintOptimizeStringConcat) {
+ tty->print_cr("Merge candidate of length %d exceeds argument limit", arguments_appended);
+ }
+#endif
+ return nullptr;
}
}
result->set_allocation(other->_begin);
@@ -680,7 +700,7 @@ PhaseStringOpts::PhaseStringOpts(PhaseGVN* gvn):
#endif
StringConcat* merged = sc->merge(other, arg);
- if (merged->validate_control_flow() && merged->validate_mem_flow()) {
+ if (merged != nullptr && merged->validate_control_flow() && merged->validate_mem_flow()) {
#ifndef PRODUCT
AtomicAccess::inc(&_stropts_merged);
if (PrintOptimizeStringConcat) {
diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsMany.java b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsMany.java
new file mode 100644
index 00000000000..dbc6e495594
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsMany.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8362394
+ * @summary Test that repeated stacked string concatenations do not
+ * consume too many compilation resources.
+ * @requires vm.compiler2.enabled
+ * @library /test/lib /
+ * @run main/othervm -XX:-OptoScheduling compiler.stringopts.TestStackedConcatsMany
+ * @run main/othervm -XX:-TieredCompilation -Xcomp -XX:-OptoScheduling
+ * -XX:CompileOnly=compiler.stringopts.TestStackedConcatsMany::f
+ * compiler.stringopts.TestStackedConcatsMany
+ */
+
+// The test uses -XX:-OptoScheduling to avoid the assert "too many D-U pinch points" on aarch64 (JDK-8328078).
+
+package compiler.stringopts;
+
+import jdk.test.lib.Asserts;
+
+public class TestStackedConcatsMany {
+
+ public static void main (String... args) {
+ new StringBuilder(); // Trigger loading of the StringBuilder class.
+ String s = f();
+ String z = "xy";
+ for (int i = 0; i < 24; i++) {
+ z = z + z;
+ }
+ Asserts.assertEQ(s, z);
+ }
+
+ static String f() {
+ String s = "xy";
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ return s;
+ }
+}
From aed9485bbb1d93063e5e5f60ed84bfb36053bdd1 Mon Sep 17 00:00:00 2001
From: Andrew Haley
Date: Tue, 7 Oct 2025 10:09:23 +0000
Subject: [PATCH 041/160] 8368303: AlwaysAtomicAccesses is excessively strict
Reviewed-by: shade, vlivanov, dlong
---
.../share/gc/shared/c1/barrierSetC1.cpp | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp
index ac640fb88d2..a31078f7e67 100644
--- a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp
+++ b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp
@@ -38,6 +38,16 @@
#define __ gen->lir()->
#endif
+// Return true iff an access to bt is single-copy atomic.
+
+// The JMM requires atomicity for all accesses to fields of primitive
+// types other than double and long. In practice, HotSpot assumes that
+// on all processors, accesses to memory operands of wordSize and
+// smaller are atomic.
+static bool access_is_atomic(BasicType bt) {
+ return type2aelembytes(bt) <= wordSize;
+}
+
LIR_Opr BarrierSetC1::resolve_address(LIRAccess& access, bool resolve_in_register) {
DecoratorSet decorators = access.decorators();
bool is_array = (decorators & IS_ARRAY) != 0;
@@ -140,7 +150,7 @@ LIR_Opr BarrierSetC1::atomic_add_at(LIRAccess& access, LIRItem& value) {
void BarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) {
DecoratorSet decorators = access.decorators();
bool is_volatile = (decorators & MO_SEQ_CST) != 0;
- bool is_atomic = is_volatile || AlwaysAtomicAccesses;
+ bool needs_atomic = AlwaysAtomicAccesses && !access_is_atomic(value->type());
bool needs_patching = (decorators & C1_NEEDS_PATCHING) != 0;
bool mask_boolean = (decorators & C1_MASK_BOOLEAN) != 0;
LIRGenerator* gen = access.gen();
@@ -154,7 +164,7 @@ void BarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) {
}
LIR_PatchCode patch_code = needs_patching ? lir_patch_normal : lir_patch_none;
- if (is_atomic && !needs_patching) {
+ if ((is_volatile || needs_atomic) && !needs_patching) {
gen->volatile_field_store(value, access.resolved_addr()->as_address_ptr(), access.access_emit_info());
} else {
__ store(value, access.resolved_addr()->as_address_ptr(), access.access_emit_info(), patch_code);
@@ -169,7 +179,7 @@ void BarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) {
LIRGenerator *gen = access.gen();
DecoratorSet decorators = access.decorators();
bool is_volatile = (decorators & MO_SEQ_CST) != 0;
- bool is_atomic = is_volatile || AlwaysAtomicAccesses;
+ bool needs_atomic = AlwaysAtomicAccesses && !access_is_atomic(result->type());
bool needs_patching = (decorators & C1_NEEDS_PATCHING) != 0;
bool mask_boolean = (decorators & C1_MASK_BOOLEAN) != 0;
bool in_native = (decorators & IN_NATIVE) != 0;
@@ -181,7 +191,7 @@ void BarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) {
LIR_PatchCode patch_code = needs_patching ? lir_patch_normal : lir_patch_none;
if (in_native) {
__ move_wide(access.resolved_addr()->as_address_ptr(), result);
- } else if (is_atomic && !needs_patching) {
+ } else if ((is_volatile || needs_atomic) && !needs_patching) {
gen->volatile_field_load(access.resolved_addr()->as_address_ptr(), result, access.access_emit_info());
} else {
__ load(access.resolved_addr()->as_address_ptr(), result, access.access_emit_info(), patch_code);
From 6bec42adcc1d99e16ddd5148bb4012c74a0c3090 Mon Sep 17 00:00:00 2001
From: Alexey Ivanov
Date: Tue, 7 Oct 2025 10:21:33 +0000
Subject: [PATCH 042/160] 8368892: Make JEditorPane/TestBrowserBGColor.java
headless
Reviewed-by: serb, azvegint
---
.../swing/JEditorPane/TestBrowserBGColor.java | 139 ++++++++----------
1 file changed, 62 insertions(+), 77 deletions(-)
diff --git a/test/jdk/javax/swing/JEditorPane/TestBrowserBGColor.java b/test/jdk/javax/swing/JEditorPane/TestBrowserBGColor.java
index bff0f7c4aaa..9abe286a75d 100644
--- a/test/jdk/javax/swing/JEditorPane/TestBrowserBGColor.java
+++ b/test/jdk/javax/swing/JEditorPane/TestBrowserBGColor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2025, Oracle and/or its 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
@@ -23,94 +23,79 @@
/*
* @test
- * @key headful
* @bug 8213781
* @summary Verify webpage background color renders correctly in JEditorPane
*/
-import java.awt.Toolkit;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import javax.swing.JDialog;
-import javax.swing.JEditorPane;
-import javax.swing.JFrame;
-import javax.swing.JScrollPane;
-import javax.swing.SwingUtilities;
-import javax.swing.event.HyperlinkEvent;
-import javax.swing.event.HyperlinkListener;
-import javax.swing.text.html.HTMLFrameHyperlinkEvent;
-import javax.swing.text.html.HTMLDocument;
import java.awt.Color;
-import java.awt.Insets;
-import java.awt.Point;
-import java.awt.Robot;
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.stream.IntStream;
-public class TestBrowserBGColor extends JFrame implements HyperlinkListener {
+import javax.imageio.ImageIO;
+import javax.swing.JEditorPane;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.View;
- private static TestBrowserBGColor b;
- private static JEditorPane browser;
+import static java.awt.image.BufferedImage.TYPE_INT_RGB;
+import static java.lang.Integer.toHexString;
+
+public final class TestBrowserBGColor {
+
+ private static final String HTML_DOC =
+ ""
+ + ""
+ + ""
+ + "Title"
+ + " ";
+
+ private static final int SIZE = 300;
public static void main(final String[] args) throws Exception {
- Robot r = new Robot();
- SwingUtilities.invokeAndWait(() -> {
- try {
- b = new TestBrowserBGColor();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- b.setSize(Toolkit.getDefaultToolkit().getScreenSize());
- b.setVisible(true);
- b.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
- b.addWindowListener(new WindowAdapter() {
- public void windowClosing(WindowEvent e) {
- b.dispose();
- b = null;
- }
- });
- });
-
- r.waitForIdle();
- r.delay(500);
-
- SwingUtilities.invokeAndWait(() -> {
- Insets insets = browser.getInsets();
- Point loc = browser.getLocationOnScreen();
- Color c = r.getPixelColor( loc.x + insets.left+100,
- loc.y + insets.top + 100);
- b.dispose();
- if (!c.equals(Color.WHITE)) {
- throw new RuntimeException("webpage background color wrong");
- }
- });
- }
-
-
- String htmlDoc = " Title ";
-
- public TestBrowserBGColor() throws IOException, MalformedURLException {
- browser = new JEditorPane("text/html", htmlDoc);
+ JEditorPane browser = new JEditorPane("text/html", HTML_DOC);
browser.setEditable(false);
- browser.addHyperlinkListener(this);
- JScrollPane scroll = new JScrollPane(browser);
- getContentPane().add(scroll);
+ browser.setSize(SIZE, SIZE);
+
+ BufferedImage image = new BufferedImage(SIZE, SIZE, TYPE_INT_RGB);
+ Graphics g = image.getGraphics();
+ browser.paint(g);
+ g.dispose();
+
+ Color bgColor = StyleConstants.getBackground(
+ getBodyView(browser.getUI()
+ .getRootView(browser))
+ .getAttributes());
+ if (!bgColor.equals(Color.WHITE)) {
+ saveImage(image);
+ throw new RuntimeException("Wrong background color: "
+ + toHexString(bgColor.getRGB())
+ + " vs "
+ + toHexString(Color.WHITE.getRGB()));
+ }
}
- public void hyperlinkUpdate(final HyperlinkEvent e) {
- if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
- JEditorPane pane = (JEditorPane) e.getSource();
- if (e instanceof HTMLFrameHyperlinkEvent) {
- HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent) e;
- HTMLDocument doc = (HTMLDocument) pane.getDocument();
- doc.processHTMLFrameHyperlinkEvent(evt);
- } else {
- try {
- pane.setPage(e.getURL());
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
+ private static View getBodyView(final View view) {
+ if ("body".equals(view.getElement()
+ .getName())) {
+ return view;
+ }
+
+ return IntStream.range(0, view.getViewCount())
+ .mapToObj(view::getView)
+ .map(TestBrowserBGColor::getBodyView)
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElse(null);
+ }
+
+ private static void saveImage(BufferedImage image) {
+ try {
+ ImageIO.write(image, "png",
+ new File("html-rendering.png"));
+ } catch (IOException ignored) {
}
}
}
From 9c46febcac01b9f1831f5f3e2a68dd1f1612a01f Mon Sep 17 00:00:00 2001
From: Yasumasa Suenaga
Date: Tue, 7 Oct 2025 12:47:40 +0000
Subject: [PATCH 043/160] 8245234: Still seeing missing mixed stack traces,
even after JDK-8234624
Reviewed-by: kevinw, cjplummer
---
.../linux/amd64/LinuxAMD64CFrame.java | 19 +++++++------------
1 file changed, 7 insertions(+), 12 deletions(-)
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java
index 5f3c9786d6e..3dfb83c9f5a 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2025, Oracle and/or its 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
@@ -145,17 +145,12 @@ public final class LinuxAMD64CFrame extends BasicCFrame {
}
DwarfParser nextDwarf = null;
-
- if ((dwarf != null) && dwarf.isIn(nextPC)) {
- nextDwarf = dwarf;
- } else {
- Address libptr = dbg.findLibPtrByAddress(nextPC);
- if (libptr != null) {
- try {
- nextDwarf = new DwarfParser(libptr);
- } catch (DebuggerException e) {
- // Bail out to Java frame
- }
+ Address libptr = dbg.findLibPtrByAddress(nextPC);
+ if (libptr != null) {
+ try {
+ nextDwarf = new DwarfParser(libptr);
+ } catch (DebuggerException e) {
+ // Bail out to Java frame
}
}
From 4b4d0cd35a32448e4b056109c502af2765766432 Mon Sep 17 00:00:00 2001
From: Johny Jose
Date: Tue, 7 Oct 2025 13:13:42 +0000
Subject: [PATCH 044/160] 8365398: TEST_BUG:
java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java failing
intermittently
Reviewed-by: msheppar, smarks, jpai
---
test/jdk/ProblemList.txt | 1 -
.../checkLeaseInfoLeak/CheckLeaseLeak.java | 25 ++++++++++++++-----
2 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt
index 8d13670805f..48a495bc238 100644
--- a/test/jdk/ProblemList.txt
+++ b/test/jdk/ProblemList.txt
@@ -608,7 +608,6 @@ java/rmi/transport/rapidExportUnexport/RapidExportUnexport.java 7146541 linux-al
java/rmi/registry/readTest/CodebaseTest.java 8173324 windows-all
java/rmi/registry/multipleRegistries/MultipleRegistries.java 8268182 macosx-all
-java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java 8365398 generic-all
java/rmi/Naming/DefaultRegistryPort.java 8005619 windows-all
java/rmi/Naming/legalRegistryNames/LegalRegistryNames.java 8005619 windows-all
diff --git a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java
index 4de6598a0f4..f40502601d2 100644
--- a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java
+++ b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java
@@ -59,10 +59,13 @@ import java.io.*;
import java.lang.reflect.*;
import java.rmi.registry.*;
import sun.rmi.transport.*;
+import java.util.concurrent.CountDownLatch;
public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak {
public CheckLeaseLeak() throws RemoteException { }
- public void ping () throws RemoteException { }
+ public void ping () throws RemoteException {
+ remoteCallsComplete.countDown();
+ }
/**
* Id to fake the DGC_ID, so we can later get a reference to the
@@ -74,6 +77,9 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak {
private final static int numberPingCalls = 0;
private final static int CHECK_INTERVAL = 400;
private final static int LEASE_VALUE = 20;
+ private static final int NO_OF_CLIENTS = ITERATIONS;
+ private static final int GOOD_LUCK_FACTOR = 2;
+ private static CountDownLatch remoteCallsComplete = new CountDownLatch(NO_OF_CLIENTS);
public static void main (String[] args) {
CheckLeaseLeak leakServer = null;
@@ -113,8 +119,14 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak {
jvm.destroy();
}
}
+ try {
+ remoteCallsComplete.await();
+ System.out.println("remoteCallsComplete . . . ");
+ } catch (InterruptedException intEx) {
+ System.out.println("remoteCallsComplete.await interrupted . . . ");
+ }
+ Thread.sleep(NO_OF_CLIENTS * LEASE_VALUE * GOOD_LUCK_FACTOR);
numLeft = getDGCLeaseTableSize();
- Thread.sleep(3000);
} catch(Exception e) {
TestLibrary.bomb("CheckLeaseLeak Error: ", e);
@@ -125,8 +137,8 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak {
}
}
- /* numLeft should be 4 - if 11 there is a problem. */
- if (numLeft > 4) {
+ /* numLeft should not be greater than 2 - if 11 there is a problem. */
+ if (numLeft > 2) {
TestLibrary.bomb("Too many objects in DGCImpl.leaseTable: "+
numLeft);
} else {
@@ -204,8 +216,9 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak {
* objects if the LeaseInfo memory leak is not fixed.
*/
leaseTable = (Map) f.get(dgcImpl[0]);
-
- numLeaseInfosLeft = leaseTable.size();
+ synchronized (leaseTable) {
+ numLeaseInfosLeft = leaseTable.size();
+ }
} catch(Exception e) {
TestLibrary.bomb(e);
From a9c93f865bb5438420bc4df278d211ff3af9a0ad Mon Sep 17 00:00:00 2001
From: Albert Mingkun Yang
Date: Tue, 7 Oct 2025 13:40:19 +0000
Subject: [PATCH 045/160] 8369263: Parallel: Inline
PSPromotionManager::push_depth
Reviewed-by: iwalulya, shade, fandreuzzi
---
src/hotspot/share/gc/parallel/psPromotionManager.hpp | 2 --
src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp | 6 +-----
2 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.hpp
index 7d3a1682519..9808a55335d 100644
--- a/src/hotspot/share/gc/parallel/psPromotionManager.hpp
+++ b/src/hotspot/share/gc/parallel/psPromotionManager.hpp
@@ -102,8 +102,6 @@ class PSPromotionManager {
void process_array_chunk(PartialArrayState* state, bool stolen);
void push_objArray(oop old_obj, oop new_obj);
- void push_depth(ScannerTask task);
-
inline void promotion_trace_event(oop new_obj, Klass* klass, size_t obj_size,
uint age, bool tenured,
const PSPromotionLAB* lab);
diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp
index 4c12a4c357f..fb58c22cf29 100644
--- a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp
+++ b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp
@@ -50,10 +50,6 @@ inline PSPromotionManager* PSPromotionManager::manager_array(uint index) {
return &_manager_array[index];
}
-inline void PSPromotionManager::push_depth(ScannerTask task) {
- claimed_stack_depth()->push(task);
-}
-
template
inline void PSPromotionManager::claim_or_forward_depth(T* p) {
assert(ParallelScavengeHeap::heap()->is_in(p), "pointer outside heap");
@@ -62,7 +58,7 @@ inline void PSPromotionManager::claim_or_forward_depth(T* p) {
oop obj = CompressedOops::decode_not_null(heap_oop);
assert(!PSScavenge::is_obj_in_to_space(obj), "revisiting object?");
Prefetch::write(obj->base_addr(), oopDesc::mark_offset_in_bytes());
- push_depth(ScannerTask(p));
+ claimed_stack_depth()->push(ScannerTask(p));
}
}
From 0f2a95c15d7c1e3796660d786c9a72497dab5ab1 Mon Sep 17 00:00:00 2001
From: jonghoonpark
Date: Tue, 7 Oct 2025 15:13:23 +0000
Subject: [PATCH 046/160] 8365782: Remove unnecessary inclusion of
in jfrOSInterface.cpp
Reviewed-by: ayang, tschatzl
---
src/hotspot/share/jfr/periodic/jfrOSInterface.cpp | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp b/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp
index 2d4b99d59ab..18b2d7c5785 100644
--- a/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp
+++ b/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp
@@ -32,8 +32,6 @@
#include "runtime/vm_version.hpp"
#include "utilities/ostream.hpp"
-#include // for environment variables
-
static JfrOSInterface* _instance = nullptr;
JfrOSInterface& JfrOSInterface::instance() {
@@ -81,10 +79,7 @@ class JfrOSInterface::JfrOSInterfaceImpl : public JfrCHeapObj {
// os information
int os_version(char** os_version) const;
- // environment information
- void generate_environment_variables_events();
-
- // system processes information
+ // system processes information
int system_processes(SystemProcess** system_processes, int* no_of_sys_processes);
int network_utilization(NetworkInterface** network_interfaces);
From 8a20656ed03aa26806c7b4a4e361999dea62aa79 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?=
Date: Tue, 7 Oct 2025 15:16:08 +0000
Subject: [PATCH 047/160] 8367321: Fix CSS bugs in dark theme 8366942: Dark
mode pages briefly blink before going dark
Reviewed-by: nbenalla, liach
---
.../doclets/formats/html/markup/Head.java | 3 +-
.../formats/html/resources/script.js.template | 60 +++++++++++--------
.../formats/html/resources/stylesheet.css | 58 +++++++++---------
.../javadoc/doclet/testSearch/TestSearch.java | 3 +-
4 files changed, 69 insertions(+), 55 deletions(-)
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java
index 2b6bfa77951..cda4bc9a5be 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java
@@ -378,7 +378,8 @@ public class Head extends Content {
mainBodyScript.append("const pathtoroot = ")
.appendStringLiteral(ptrPath + "/")
.append(";\n")
- .append("loadScripts(document, 'script');");
+ .append("loadScripts();\n")
+ .append("initTheme();\n");
}
addScriptElement(head, DocPaths.JQUERY_JS);
addScriptElement(head, DocPaths.JQUERY_UI_JS);
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template
index b275323b2f5..b91f99b2c42 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template
@@ -11,12 +11,13 @@ var typeSearchIndex;
var memberSearchIndex;
var tagSearchIndex;
-var oddRowColor = "odd-row-color";
-var evenRowColor = "even-row-color";
-var sortAsc = "sort-asc";
-var sortDesc = "sort-desc";
-var tableTab = "table-tab";
-var activeTableTab = "active-table-tab";
+const oddRowColor = "odd-row-color";
+const evenRowColor = "even-row-color";
+const sortAsc = "sort-asc";
+const sortDesc = "sort-desc";
+const tableTab = "table-tab";
+const activeTableTab = "active-table-tab";
+const THEMES = Object.freeze(["theme-light", "theme-dark", "theme-os"]);
const linkIcon = "##REPLACE:doclet.Link_icon##";
const linkToSection = "##REPLACE:doclet.Link_to_section##";
@@ -30,21 +31,20 @@ if (typeof hljs !== "undefined") {
}
}
-function loadScripts(doc, tag) {
- createElem(doc, tag, 'script-files/search.js');
-
- createElem(doc, tag, 'module-search-index.js');
- createElem(doc, tag, 'package-search-index.js');
- createElem(doc, tag, 'type-search-index.js');
- createElem(doc, tag, 'member-search-index.js');
- createElem(doc, tag, 'tag-search-index.js');
+function loadScripts() {
+ createScript('script-files/search.js');
+ createScript('module-search-index.js');
+ createScript('package-search-index.js');
+ createScript('type-search-index.js');
+ createScript('member-search-index.js');
+ createScript('tag-search-index.js');
}
-function createElem(doc, tag, path) {
- var script = doc.createElement(tag);
- var scriptElement = doc.getElementsByTagName(tag)[0];
+function createScript(path) {
+ var script = document.createElement("script");
script.src = pathtoroot + path;
- scriptElement.parentNode.insertBefore(script, scriptElement);
+ var firstScript = document.getElementsByTagName("script")[0];
+ firstScript.parentNode.insertBefore(script, firstScript);
}
// Helper for making content containing release names comparable lexicographically
@@ -312,21 +312,31 @@ function makeFilterWidget(sidebar, updateToc) {
return sidebar;
}
+function getTheme() {
+ return localStorage.getItem('theme') || THEMES[0];
+}
+
+function initTheme() {
+ document.body.classList.add(getTheme());
+}
+
function setTopMargin() {
// Dynamically set scroll margin to accomodate for draft header
var headerHeight = Math.ceil(document.querySelector("header").offsetHeight);
document.querySelector(":root")
.style.setProperty("--nav-height", headerHeight + "px");
}
+
document.addEventListener("readystatechange", (e) => {
if (document.readyState === "interactive") {
setTopMargin();
- }
- if (sessionStorage.getItem("sidebar") === "hidden") {
- const sidebar = document.querySelector(".main-grid nav.toc");
- if (sidebar) sidebar.classList.add("hide-sidebar");
+ if (sessionStorage.getItem("sidebar") === "hidden") {
+ const sidebar = document.querySelector(".main-grid nav.toc");
+ if (sidebar) sidebar.classList.add("hide-sidebar");
+ }
}
});
+
document.addEventListener("DOMContentLoaded", function(e) {
setTopMargin();
const subnav = document.querySelector("ol.sub-nav-list");
@@ -375,13 +385,16 @@ document.addEventListener("DOMContentLoaded", function(e) {
themePanelVisible = false;
}
}
+ var currentTheme = getTheme();
themePanel.querySelectorAll("input").forEach(input => {
input.removeAttribute("disabled");
+ if (input.id === currentTheme) {
+ input.checked = true;
+ }
input.addEventListener("change", e => {
setTheme(e.target.value);
})
});
- const THEMES = ["theme-light", "theme-dark", "theme-os"];
function setTheme(theme) {
THEMES.forEach(t => {
if (t !== theme) document.body.classList.remove(t);
@@ -390,7 +403,6 @@ document.addEventListener("DOMContentLoaded", function(e) {
localStorage.setItem("theme", theme);
document.getElementById(theme).checked = true;
}
- setTheme(localStorage.getItem("theme") || THEMES[0]);
makeFilterWidget(sidebar, updateToc);
if (tocMenu) {
navbar.appendChild(tocMenu);
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css
index d61283dc677..4bb0fad4306 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css
@@ -73,7 +73,7 @@ body {
--selected-link-color: #4a698a;
/* Background colors for generated tables */
--table-header-color: #ebeff4;
- --even-row-color: #ffffff;
+ --even-row-color: #fdfdfe;
--odd-row-color: #f0f0f2;
/* Text color for page title */
--title-color: #2c4557;
@@ -109,17 +109,19 @@ body {
/* Colors for invalid tag notifications */
--invalid-tag-background-color: #ffe6e6;
--invalid-tag-text-color: #000000;
+ --icon-filter: none;
+ --caption-link-color: var(--subnav-link-color);
}
body.theme-dark {
--body-text-color: #e8e8e8;
--block-text-color: #e8e8e8;
- --body-background-color: #222528;
+ --body-background-color: #1f2124;
--section-background-color: var(--body-background-color);
--detail-background-color: var(--body-background-color);
--code-background-color: #303940;
--mark-background-color: #313131;
- --detail-block-color: #f4f4f4;
+ --detail-block-color: #31363c;
--navbar-background-color: #395A6F;
--navbar-text-color: #ffffff;
--subnav-background-color: #3d454d;
@@ -133,11 +135,11 @@ body.theme-dark {
--odd-row-color: #2d3135;
--title-color: #fff;
--link-color: #94badb;
- --link-color-active: #ffb45b;
- --toc-background-color: #31363c;
+ --link-color-active: #e8a351;
+ --toc-background-color: #2f3439;
--toc-highlight-color: var(--subnav-background-color);
--toc-hover-color: #3f4146;
- --snippet-background-color: #2d363c;
+ --snippet-background-color: #2c353b;
--snippet-text-color: var(--block-text-color);
--snippet-highlight-color: #f7c590;
--pre-background-color: var(--snippet-background-color);
@@ -155,10 +157,8 @@ body.theme-dark {
--button-focus-filter: brightness(104%);
--invalid-tag-background-color: #ffe6e6;
--invalid-tag-text-color: #000000;
- div.main-grid img,
- .inherited-list h3 > button {
- filter: invert(100%) brightness(160%);
- }
+ --icon-filter: invert(100%) brightness(160%);
+ --caption-link-color: var(--link-color);
}
/*
@@ -168,12 +168,12 @@ body.theme-dark {
body {
--body-text-color: #e8e8e8;
--block-text-color: #e8e8e8;
- --body-background-color: #222528;
+ --body-background-color: #1f2124;
--section-background-color: var(--body-background-color);
--detail-background-color: var(--body-background-color);
--code-background-color: #303940;
--mark-background-color: #313131;
- --detail-block-color: #f4f4f4;
+ --detail-block-color: #31363c;
--navbar-background-color: #395A6F;
--navbar-text-color: #ffffff;
--subnav-background-color: #3d454d;
@@ -187,11 +187,11 @@ body.theme-dark {
--odd-row-color: #2d3135;
--title-color: #fff;
--link-color: #94badb;
- --link-color-active: #ffb45b;
- --toc-background-color: #31363c;
+ --link-color-active: #e8a351;
+ --toc-background-color: #2f3439;
--toc-highlight-color: var(--subnav-background-color);
--toc-hover-color: #3f4146;
- --snippet-background-color: #2d363c;
+ --snippet-background-color: #2c353b;
--snippet-text-color: var(--block-text-color);
--snippet-highlight-color: #f7c590;
--pre-background-color: var(--snippet-background-color);
@@ -209,15 +209,13 @@ body.theme-dark {
--button-focus-filter: brightness(104%);
--invalid-tag-background-color: #ffe6e6;
--invalid-tag-text-color: #000000;
- div.main-grid img,
- .inherited-list h3 > button {
- filter: invert(100%) brightness(160%);
- }
+ --icon-filter: invert(100%) brightness(160%);
+ --caption-link-color: var(--link-color);
}
body.theme-light {
- --body-text-color: #282828;
- --block-text-color: #282828;
+ --body-text-color: #181818;
+ --block-text-color: #181818;
--body-background-color: #ffffff;
--section-background-color: var(--body-background-color);
--detail-background-color: var(--body-background-color);
@@ -233,7 +231,7 @@ body.theme-dark {
--selected-text-color: #253441;
--selected-link-color: #4a698a;
--table-header-color: #ebeff4;
- --even-row-color: #ffffff;
+ --even-row-color: #fdfdfe;
--odd-row-color: #f0f0f2;
--title-color: #2c4557;
--link-color: #437291;
@@ -259,10 +257,8 @@ body.theme-dark {
--button-focus-filter: brightness(104%);
--invalid-tag-background-color: #ffe6e6;
--invalid-tag-text-color: #000000;
- div.main-grid img,
- .inherited-list h3 > button {
- filter: none;
- }
+ --icon-filter: none;
+ --caption-link-color: var(--subnav-link-color);
}
}
/*
@@ -288,6 +284,9 @@ div.main-grid {
max-width: var(--max-content-width);
margin: var(--content-margin);
}
+div.main-grid img {
+ filter: var(--icon-filter);
+}
a:link, a:visited {
text-decoration:none;
color:var(--link-color);
@@ -909,12 +908,12 @@ ul.preview-feature-list input {
.caption a:visited,
.inherited-list h3 a:link,
.inherited-list h3 a:visited {
- color:var(--subnav-link-color);
+ color:var(--caption-link-color);
}
.caption a:hover,
.caption a:active,
-.inherited-list.expanded h3 a:hover,
-.inherited-list.expanded h3 a:active {
+.inherited-list h3 a:hover,
+.inherited-list h3 a:active {
color: var(--link-color-active);
}
div.table-tabs {
@@ -1539,6 +1538,7 @@ section[class$="-details"] .detail > div {
height: 1.6em;
vertical-align: middle;
top: -2px;
+ filter: var(--icon-filter);
}
.inherited-list h3:has(button) {
padding-left: 2px;
diff --git a/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java b/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java
index 8615d9f1e64..00c2f017909 100644
--- a/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java
+++ b/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java
@@ -427,7 +427,8 @@ public class TestSearch extends JavadocTester {
""",
"""
const pathtoroot = "./";
- loadScripts(document, 'script');""",
+ loadScripts();
+ initTheme();""",
"",
"""
Search""",
From eb729f0aaa2297c3b3dbadadf40a502d2d9ed124 Mon Sep 17 00:00:00 2001
From: Erik Gahlin
Date: Tue, 7 Oct 2025 15:38:58 +0000
Subject: [PATCH 048/160] 8247776: JFR: TestThreadContextSwitches.java failed
"RuntimeException: No events: expected false, was true"
Reviewed-by: mgronlun
---
.../event/os/TestThreadContextSwitches.java | 29 +++++++++++++------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/test/jdk/jdk/jfr/event/os/TestThreadContextSwitches.java b/test/jdk/jdk/jfr/event/os/TestThreadContextSwitches.java
index 7b3dfc79ce9..acfaecdde7d 100644
--- a/test/jdk/jdk/jfr/event/os/TestThreadContextSwitches.java
+++ b/test/jdk/jdk/jfr/event/os/TestThreadContextSwitches.java
@@ -28,6 +28,7 @@ import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedEvent;
import jdk.test.lib.jfr.EventNames;
import jdk.test.lib.jfr.Events;
+import jdk.test.lib.Platform;
/**
* @test
@@ -40,15 +41,25 @@ public class TestThreadContextSwitches {
private final static String EVENT_NAME = EventNames.ThreadContextSwitchRate;
public static void main(String[] args) throws Throwable {
- Recording recording = new Recording();
- recording.enable(EVENT_NAME);
- recording.start();
- recording.stop();
- List events = Events.fromRecording(recording);
- Events.hasEvents(events);
- for (RecordedEvent event : events) {
- System.out.println("Event: " + event);
- Events.assertField(event, "switchRate").atLeast(0.0f);
+ while (true) {
+ try (Recording recording = new Recording()) {
+ recording.enable(EVENT_NAME);
+ recording.start();
+ recording.stop();
+ List events = Events.fromRecording(recording);
+ if (!events.isEmpty()) {
+ for (RecordedEvent event : events) {
+ System.out.println("Event: " + event);
+ Events.assertField(event, "switchRate").atLeast(0.0f);
+ }
+ return;
+ }
+ // Thread context switch rate is unreliable on Windows because
+ // the way processes are identified with performance counters.
+ if (!Platform.isWindows()) {
+ Events.hasEvents(events);
+ }
+ }
}
}
}
From eb835e05f9cf8a65d804b733b382ecfba5b12907 Mon Sep 17 00:00:00 2001
From: Volkan Yazici
Date: Tue, 7 Oct 2025 15:57:31 +0000
Subject: [PATCH 049/160] 8366040: Change URL.lookupViaProviders to use
ScopedValue to detect recursive lookup
Reviewed-by: alanb, dfuchs
---
src/java.base/share/classes/java/net/URL.java | 22 ++-------
.../spi/URLStreamHandlerProvider/Basic.java | 18 ++++++-
.../circular.provider.template | 48 +++++++++++++++++++
3 files changed, 69 insertions(+), 19 deletions(-)
create mode 100644 test/jdk/java/net/spi/URLStreamHandlerProvider/circular.provider.template
diff --git a/src/java.base/share/classes/java/net/URL.java b/src/java.base/share/classes/java/net/URL.java
index 1435d851f41..c82236b5b85 100644
--- a/src/java.base/share/classes/java/net/URL.java
+++ b/src/java.base/share/classes/java/net/URL.java
@@ -41,7 +41,6 @@ import java.util.ServiceLoader;
import jdk.internal.access.JavaNetURLAccess;
import jdk.internal.access.SharedSecrets;
-import jdk.internal.misc.ThreadTracker;
import jdk.internal.misc.VM;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
@@ -1394,24 +1393,13 @@ public final class URL implements java.io.Serializable {
return handler;
}
- private static class ThreadTrackHolder {
- static final ThreadTracker TRACKER = new ThreadTracker();
- }
-
- private static Object tryBeginLookup() {
- return ThreadTrackHolder.TRACKER.tryBegin();
- }
-
- private static void endLookup(Object key) {
- ThreadTrackHolder.TRACKER.end(key);
- }
+ private static final ScopedValue IN_LOOKUP = ScopedValue.newInstance();
private static URLStreamHandler lookupViaProviders(final String protocol) {
- Object key = tryBeginLookup();
- if (key == null) {
+ if (IN_LOOKUP.isBound()) {
throw new Error("Circular loading of URL stream handler providers detected");
}
- try {
+ return ScopedValue.where(IN_LOOKUP, true).call(() -> {
final ClassLoader cl = ClassLoader.getSystemClassLoader();
final ServiceLoader sl =
ServiceLoader.load(URLStreamHandlerProvider.class, cl);
@@ -1423,9 +1411,7 @@ public final class URL implements java.io.Serializable {
return h;
}
return null;
- } finally {
- endLookup(key);
- }
+ });
}
/**
diff --git a/test/jdk/java/net/spi/URLStreamHandlerProvider/Basic.java b/test/jdk/java/net/spi/URLStreamHandlerProvider/Basic.java
index 9f8381a92c8..a98bf8e129e 100644
--- a/test/jdk/java/net/spi/URLStreamHandlerProvider/Basic.java
+++ b/test/jdk/java/net/spi/URLStreamHandlerProvider/Basic.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its 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
@@ -77,6 +77,7 @@ public class Basic {
viaProvider("bert", KNOWN);
viaBadProvider("tom", SCE);
viaBadProvider("jerry", SCE);
+ viaCircularProvider("circular", CIRCULAR);
}
private static String withoutWarning(String in) {
@@ -99,6 +100,12 @@ public class Basic {
throw new RuntimeException("exitValue: "+ r.exitValue + ", output:[" +r.output +"]");
}
};
+ static final Consumer CIRCULAR = r -> {
+ if (r.exitValue == 0 ||
+ !r.output.contains("Circular loading of URL stream handler providers detected")) {
+ throw new RuntimeException("exitValue: " + r.exitValue + ", output:[" + r.output + "]");
+ }
+ };
static void unknownProtocol(String protocol, Consumer resultChecker) {
System.out.println("\nTesting " + protocol);
@@ -125,6 +132,15 @@ public class Basic {
sysProps);
}
+ static void viaCircularProvider(String protocol, Consumer resultChecker,
+ String... sysProps)
+ throws Exception
+ {
+ viaProviderWithTemplate(protocol, resultChecker,
+ TEST_SRC.resolve("circular.provider.template"),
+ sysProps);
+ }
+
static void viaProviderWithTemplate(String protocol,
Consumer resultChecker,
Path template, String... sysProps)
diff --git a/test/jdk/java/net/spi/URLStreamHandlerProvider/circular.provider.template b/test/jdk/java/net/spi/URLStreamHandlerProvider/circular.provider.template
new file mode 100644
index 00000000000..b846cc4fec6
--- /dev/null
+++ b/test/jdk/java/net/spi/URLStreamHandlerProvider/circular.provider.template
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its 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.
+ *
+ * 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 $package;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.net.spi.URLStreamHandlerProvider;
+
+public final class Provider extends URLStreamHandlerProvider {
+
+ private static final String PROTOCOL = "$protocol";
+
+ @Override
+ public URLStreamHandler createURLStreamHandler(String protocol) {
+ try {
+ // Trigger circular lookup
+ URI.create("bogus://path/to/nothing").toURL();
+ } catch (Exception exception) {
+ throw new RuntimeException(exception);
+ }
+ throw new AssertionError("Should not have reached here!");
+ }
+
+}
From 4ca3ab62759b366fd3e0b2267925f1fa70f057b7 Mon Sep 17 00:00:00 2001
From: Joe Darcy
Date: Tue, 7 Oct 2025 16:41:45 +0000
Subject: [PATCH 050/160] 8369123: Still more small Float16 refactorings
Reviewed-by: rgiulietti
---
.../classes/jdk/incubator/vector/Float16.java | 52 ++++++++++++-------
1 file changed, 32 insertions(+), 20 deletions(-)
diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16.java
index a564cdfed0f..fe2a6bf5580 100644
--- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16.java
+++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16.java
@@ -35,6 +35,8 @@ import static jdk.incubator.vector.Float16Consts.SIGN_BIT_MASK;
import static jdk.incubator.vector.Float16Consts.EXP_BIT_MASK;
import static jdk.incubator.vector.Float16Consts.SIGNIF_BIT_MASK;
import static jdk.incubator.vector.Float16Consts.MAG_BIT_MASK;
+import static jdk.incubator.vector.Float16Consts.EXP_BIAS;
+import static jdk.incubator.vector.Float16Consts.SIGNIFICAND_WIDTH;
import static java.lang.Float.float16ToFloat;
import static java.lang.Float.floatToFloat16;
@@ -95,19 +97,26 @@ import jdk.internal.vm.vector.Float16Math;
* IEEE Standard for Floating-Point Arithmetic
*/
-// Currently Float16 is a value-based class and in future it is
+// Currently Float16 is a value-based class and in the future it is
// expected to be aligned with Value Classes and Object as described in
// JEP-401 (https://openjdk.org/jeps/401).
@jdk.internal.ValueBased
public final class Float16
extends Number
implements Comparable {
- /** @serial */
+
+ /**
+ * Primitive {@code short} field to hold the bits of the {@code Float16}.
+ * @serial
+ */
private final short value;
+
private static final long serialVersionUID = 16; // May not be needed when a value class?
// Functionality for future consideration:
- // IEEEremainder / remainder operator remainder
+ // IEEEremainder and separate % operator remainder (which are
+ // defined to use different rounding modes, see JLS sections 15.4
+ // and 15.17.3).
// Do *not* define any public constructors
/**
@@ -147,8 +156,14 @@ public final class Float16
*/
public static final Float16 NaN = valueOf(Float.NaN);
+ /**
+ * A constant holding a zero (0.0) of type {@code Float16}.
+ */
private static final Float16 ZERO = valueOf(0);
+ /**
+ * A constant holding a one (1.0) of type {@code Float16}.
+ */
private static final Float16 ONE = valueOf(1);
/**
@@ -316,10 +331,10 @@ public final class Float16
* @param value a {@code long} value.
*/
public static Float16 valueOf(long value) {
- if (value <= -65_520L) { // -(Float16.MAX_VALUE + Float16.ulp(Float16.MAX_VALUE) / 2)
+ if (value <= -65_520L) { // -(MAX_VALUE + ulp(MAX_VALUE) / 2)
return NEGATIVE_INFINITY;
} else {
- if (value >= 65_520L) { // Float16.MAX_VALUE + Float16.ulp(Float16.MAX_VALUE) / 2
+ if (value >= 65_520L) { // MAX_VALUE + ulp(MAX_VALUE) / 2
return POSITIVE_INFINITY;
}
// Remaining range of long, the integers in approx. +/-
@@ -427,9 +442,8 @@ public final class Float16
// to implement a carry out from rounding the significand.
assert (0xf800 & signif_bits) == 0x0;
- // Exponent bias adjust in the representation is equal to MAX_EXPONENT.
return new Float16((short)(sign_bit |
- ( ((exp + MAX_EXPONENT) << (PRECISION - 1)) + signif_bits ) ));
+ ( ((exp + EXP_BIAS) << (PRECISION - 1)) + signif_bits) ));
}
/**
@@ -468,7 +482,7 @@ public final class Float16
// characters rather than codepoints.
if (trialResult == 0.0 // handles signed zeros
- || Math.abs(trialResult) > (65504.0 + 32.0) || // Float.MAX_VALUE + ulp(MAX_VALUE),
+ || Math.abs(trialResult) > (65504.0 + 32.0) || // MAX_VALUE + ulp(MAX_VALUE),
// handles infinities too
Double.isNaN(trialResult) ||
noDoubleRoundingToFloat16(trialResult)) {
@@ -899,7 +913,7 @@ public final class Float16
*/
public static int hashCode(Float16 value) {
// Use bit-pattern of canonical NaN for hashing.
- Float16 f16 = isNaN(value) ? Float16.NaN : value;
+ Float16 f16 = isNaN(value) ? NaN : value;
return (int)float16ToRawShortBits(f16);
}
@@ -946,7 +960,7 @@ public final class Float16
*/
public static short float16ToShortBits(Float16 f16) {
if (isNaN(f16)) {
- return Float16.NaN.value;
+ return NaN.value;
}
return f16.value;
}
@@ -1531,8 +1545,8 @@ public final class Float16
*/
/*package*/ static int getExponent0(short bits) {
// package private to be usable in java.lang.Float.
- int bin16ExpBits = 0x0000_7c00 & bits; // Five exponent bits.
- return (bin16ExpBits >> (PRECISION - 1)) - 15;
+ int bin16ExpBits = EXP_BIT_MASK & bits; // Five exponent bits.
+ return (bin16ExpBits >> (PRECISION - 1)) - EXP_BIAS;
}
/**
@@ -1563,10 +1577,10 @@ public final class Float16
int exp = getExponent(f16);
return switch(exp) {
- case MAX_EXPONENT + 1 -> abs(f16); // NaN or infinity
- case MIN_EXPONENT - 1 -> Float16.MIN_VALUE; // zero or subnormal
+ case MAX_EXPONENT + 1 -> abs(f16); // NaN or infinity
+ case MIN_EXPONENT - 1 -> MIN_VALUE; // zero or subnormal
default -> {
- assert exp <= MAX_EXPONENT && exp >= MIN_EXPONENT;
+ assert exp <= MAX_EXPONENT && exp >= MIN_EXPONENT: "Out of range exponent";
// ulp(x) is usually 2^(SIGNIFICAND_WIDTH-1)*(2^ilogb(x))
// Let float -> float16 conversion handle encoding issues.
yield scalb(ONE, exp - (PRECISION - 1));
@@ -1687,8 +1701,7 @@ public final class Float16
// nonzero value by it would be guaranteed to over or
// underflow; due to rounding, scaling down takes an
// additional power of two which is reflected here
- final int MAX_SCALE = Float16.MAX_EXPONENT + -Float16.MIN_EXPONENT +
- Float16Consts.SIGNIFICAND_WIDTH + 1;
+ final int MAX_SCALE = MAX_EXPONENT + -MIN_EXPONENT + SIGNIFICAND_WIDTH + 1;
// Make sure scaling factor is in a reasonable range
scaleFactor = Math.clamp(scaleFactor, -MAX_SCALE, MAX_SCALE);
@@ -1725,9 +1738,8 @@ public final class Float16
* @see Math#copySign(double, double)
*/
public static Float16 copySign(Float16 magnitude, Float16 sign) {
- return shortBitsToFloat16((short) ((float16ToRawShortBits(sign) & SIGN_BIT_MASK) |
- (float16ToRawShortBits(magnitude) &
- (EXP_BIT_MASK | SIGNIF_BIT_MASK) )));
+ return shortBitsToFloat16((short)((float16ToRawShortBits(sign) & SIGN_BIT_MASK) |
+ (float16ToRawShortBits(magnitude) & MAG_BIT_MASK)));
}
/**
From ebeb77baaeb6d9098d7462f5ddf61d8583b1e493 Mon Sep 17 00:00:00 2001
From: Phil Race
Date: Tue, 7 Oct 2025 16:47:43 +0000
Subject: [PATCH 051/160] 8358058: sun/java2d/OpenGL/DrawImageBg.java Test
fails intermittently
Reviewed-by: azvegint, serb, psadhukhan
---
test/jdk/ProblemList.txt | 1 +
.../sun/java2d/OpenGL/DrawBitmaskImage.java | 173 ++++++
test/jdk/sun/java2d/OpenGL/DrawBufImgOp.java | 514 ++++++++++++++++++
test/jdk/sun/java2d/OpenGL/DrawImageBg.java | 145 +++++
test/jdk/sun/java2d/OpenGL/LargeOps.java | 138 +++++
test/jdk/sun/java2d/OpenGL/OpaqueDest.java | 180 ++++++
.../jdk/sun/java2d/OpenGL/ScaleParamsOOB.java | 198 +++++++
test/jdk/sun/java2d/OpenGL/ShapeClip.java | 140 +++++
test/jdk/sun/java2d/OpenGL/SrcMaskOps.java | 188 +++++++
.../sun/java2d/OpenGL/VolatileSubRegion.java | 166 ++++++
test/jdk/sun/java2d/OpenGL/XformVolatile.java | 146 +++++
11 files changed, 1989 insertions(+)
create mode 100644 test/jdk/sun/java2d/OpenGL/DrawBitmaskImage.java
create mode 100644 test/jdk/sun/java2d/OpenGL/DrawBufImgOp.java
create mode 100644 test/jdk/sun/java2d/OpenGL/DrawImageBg.java
create mode 100644 test/jdk/sun/java2d/OpenGL/LargeOps.java
create mode 100644 test/jdk/sun/java2d/OpenGL/OpaqueDest.java
create mode 100644 test/jdk/sun/java2d/OpenGL/ScaleParamsOOB.java
create mode 100644 test/jdk/sun/java2d/OpenGL/ShapeClip.java
create mode 100644 test/jdk/sun/java2d/OpenGL/SrcMaskOps.java
create mode 100644 test/jdk/sun/java2d/OpenGL/VolatileSubRegion.java
create mode 100644 test/jdk/sun/java2d/OpenGL/XformVolatile.java
diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt
index 48a495bc238..fce7fe85069 100644
--- a/test/jdk/ProblemList.txt
+++ b/test/jdk/ProblemList.txt
@@ -248,6 +248,7 @@ sun/awt/datatransfer/SuplementaryCharactersTransferTest.java 8011371 generic-all
sun/awt/shell/ShellFolderMemoryLeak.java 8197794 windows-all
sun/java2d/DirectX/OverriddenInsetsTest/OverriddenInsetsTest.java 8196102 generic-all
sun/java2d/DirectX/RenderingToCachedGraphicsTest/RenderingToCachedGraphicsTest.java 8196180 windows-all,macosx-all
+sun/java2d/OpenGL/OpaqueDest.java#id1 8367574 macosx-all
sun/java2d/SunGraphics2D/EmptyClipRenderingTest.java 8144029 macosx-all,linux-all
sun/java2d/SunGraphics2D/DrawImageBilinear.java 8297175 linux-all
sun/java2d/SunGraphics2D/PolyVertTest.java 6986565 generic-all
diff --git a/test/jdk/sun/java2d/OpenGL/DrawBitmaskImage.java b/test/jdk/sun/java2d/OpenGL/DrawBitmaskImage.java
new file mode 100644
index 00000000000..d2730593b5b
--- /dev/null
+++ b/test/jdk/sun/java2d/OpenGL/DrawBitmaskImage.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2004, 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 6248561 6264014
+ * @key headful
+ * @requires (os.family != "mac")
+ * @summary Verifies that bitmask image copies work properly with the
+ * OGL pipeline when a SrcOver composite with extra alpha is involved.
+ * @run main/othervm -Dsun.java2d.opengl=True DrawBitmaskImage
+ * @run main/othervm DrawBitmaskImage
+ */
+
+/*
+ * @test
+ * @bug 6248561 6264014
+ * @key headful
+ * @requires (os.family == "mac")
+ * @summary Verifies that bitmask image copies work properly with the
+ * OGL pipeline when a SrcOver composite with extra alpha is involved.
+ * @run main/othervm -Dsun.java2d.opengl=True DrawBitmaskImage
+ * @run main/othervm DrawBitmaskImage
+ */
+
+import java.awt.AlphaComposite;
+import java.awt.Canvas;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Transparency;
+import java.awt.Robot;
+import java.awt.image.BufferedImage;
+import java.awt.image.IndexColorModel;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+public class DrawBitmaskImage extends Panel {
+
+ static final int TESTW = 200, TESTH = 200;
+ private static volatile DrawBitmaskImage test;
+ private static volatile Frame frame;
+
+ public void paint(Graphics g) {
+
+ Graphics2D g2d = (Graphics2D)g;
+ g2d.setColor(Color.black);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+ g2d.setComposite(AlphaComposite.SrcOver.derive(0.50f));
+
+ BufferedImage img = getGraphicsConfiguration().createCompatibleImage(50, 50,
+ Transparency.BITMASK);
+ Graphics2D gimg = img.createGraphics();
+ gimg.setComposite(AlphaComposite.Src);
+ gimg.setColor(new Color(0, 0, 0, 0));
+ gimg.fillRect(0, 0, 50, 50);
+ gimg.setColor(Color.red);
+ gimg.fillRect(10, 10, 30, 30);
+ gimg.dispose();
+
+
+ g2d.drawImage(img, 10, 10, null);
+
+ // draw a second time to ensure that the cached copy is used
+ g2d.drawImage(img, 80, 10, null);
+ }
+
+ public Dimension getPreferredSize() {
+ return new Dimension(TESTW, TESTH);
+ }
+
+ static void createUI() {
+ test = new DrawBitmaskImage();
+ frame = new Frame("OpenGL DrawBitmaskImage Test");
+ Panel p = new Panel();
+ p.add(test);
+ frame.add(p);
+ frame.setSize(TESTW+100, TESTH+100);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+ Robot robot = new Robot();
+
+ EventQueue.invokeAndWait(DrawBitmaskImage::createUI);
+
+ robot.waitForIdle();
+ robot.delay(2000);
+
+ BufferedImage capture = null;
+ try {
+ GraphicsConfiguration gc = frame.getGraphicsConfiguration();
+ if (gc.getColorModel() instanceof IndexColorModel) {
+ System.out.println("IndexColorModel detected: " +
+ "test considered PASSED");
+ return;
+ }
+ Point pt1 = test.getLocationOnScreen();
+ Rectangle rect = new Rectangle(pt1.x, pt1.y, TESTW, TESTH);
+ capture = robot.createScreenCapture(rect);
+ } finally {
+ if (frame != null) {
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+ }
+
+ // Test background color
+ int pixel = capture.getRGB(5, 10);
+ if (pixel != 0xff000000) {
+ saveImage(capture);
+ throw new RuntimeException("Failed: Incorrect color for " +
+ "background (actual=" +
+ Integer.toHexString(pixel) + ")");
+ }
+
+ // Test pixels (allow for small error in the actual red value)
+ pixel = capture.getRGB(25, 25);
+ System.out.println("pixel1 is " + Integer.toHexString(pixel));
+
+ if ((pixel < 0xff7e0000) || (pixel > 0xff900000)) {
+ saveImage(capture);
+ throw new RuntimeException("Failed: Incorrect color for " +
+ "first pixel (actual=" +
+ Integer.toHexString(pixel) + ")");
+ }
+
+ pixel = capture.getRGB(95, 25);
+ System.out.println("pixel2 is " + Integer.toHexString(pixel));
+ if ((pixel < 0xff7e0000) || (pixel > 0xff900000)) {
+ saveImage(capture);
+ throw new RuntimeException("Failed: Incorrect color for " +
+ "second pixel (actual=" +
+ Integer.toHexString(pixel) + ")");
+ }
+ }
+
+ static void saveImage(BufferedImage img) {
+ try {
+ File file = new File("capture.png");
+ ImageIO.write(img, "png", file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/test/jdk/sun/java2d/OpenGL/DrawBufImgOp.java b/test/jdk/sun/java2d/OpenGL/DrawBufImgOp.java
new file mode 100644
index 00000000000..5d60eb7e792
--- /dev/null
+++ b/test/jdk/sun/java2d/OpenGL/DrawBufImgOp.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright (c) 2004, 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 6514990
+ * @key headful
+ * @requires (os.family != "mac")
+ * @summary Verifies that calling
+ * Graphics2D.drawImage(BufferedImage, BufferedImageOp, x, y) to an
+ * OpenGL-accelerated destination produces the same results when performed
+ * in software via BufferedImageOp.filter().
+ * @run main/othervm -Dsun.java2d.opengl=True DrawBufImgOp -ignore
+ */
+
+/*
+ * @test
+ * @bug 6514990
+ * @key headful
+ * @requires (os.family == "mac")
+ * @summary Verifies that calling
+ * Graphics2D.drawImage(BufferedImage, BufferedImageOp, x, y) to an
+ * OpenGL-accelerated destination produces the same results when performed
+ * in software via BufferedImageOp.filter().
+ * @run main/othervm -Dsun.java2d.opengl=True DrawBufImgOp -ignore
+ */
+
+import java.awt.AlphaComposite;
+import java.awt.Canvas;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.image.BufferedImage;
+import java.awt.image.ByteLookupTable;
+import java.awt.image.ColorModel;
+import java.awt.image.ConvolveOp;
+import java.awt.image.IndexColorModel;
+import java.awt.image.Kernel;
+import java.awt.image.LookupOp;
+import java.awt.image.RescaleOp;
+import java.awt.image.ShortLookupTable;
+import java.awt.image.VolatileImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+/**
+ * REMIND: This testcase was originally intended to automatically compare
+ * the results of the software BufferedImageOp implementations against
+ * the OGL-accelerated codepaths. However, there are just too many open
+ * bugs in the mediaLib-based codepaths (see below), which means that
+ * creating the reference image may cause crashes or exceptions,
+ * and even if we work around those cases using the "-ignore" flag,
+ * the visual results of the reference image are often buggy as well
+ * (so the comparison will fail even though the OGL results are correct).
+ * Therefore, for now we will run the testcase with the "-ignore" flag
+ * but without the "-compare" flag, so at least it will be checking for
+ * any exceptions/crashes in the OGL code. When we fix all of the
+ * outstanding bugs with the software codepaths, we can remove the
+ * "-ignore" flag and maybe even restore the "-compare" flag. In the
+ * meantime, it also functions well as a manual testcase (with either
+ * the "-show" or "-dump" options).
+ */
+public class DrawBufImgOp extends Canvas {
+
+ private static final int TESTW = 600;
+ private static final int TESTH = 500;
+
+ private static volatile DrawBufImgOp test;
+ private static volatile Frame frame;
+
+ /*
+ * If true, skips tests that are known to trigger bugs (which in
+ * turn may cause crashes, exceptions, or other artifacts).
+ */
+ private static boolean ignore;
+
+ // Test both pow2 and non-pow2 sized images
+ private static final int[] srcSizes = { 32, 17 };
+ private static final int[] srcTypes = {
+ BufferedImage.TYPE_INT_RGB,
+ BufferedImage.TYPE_INT_ARGB,
+ BufferedImage.TYPE_INT_ARGB_PRE,
+ BufferedImage.TYPE_INT_BGR,
+ BufferedImage.TYPE_3BYTE_BGR,
+ BufferedImage.TYPE_4BYTE_ABGR,
+ BufferedImage.TYPE_USHORT_565_RGB,
+ BufferedImage.TYPE_BYTE_GRAY,
+ BufferedImage.TYPE_USHORT_GRAY,
+ };
+
+ private static final RescaleOp
+ rescale1band, rescale3band, rescale4band;
+ private static final LookupOp
+ lookup1bandbyte, lookup3bandbyte, lookup4bandbyte;
+ private static final LookupOp
+ lookup1bandshort, lookup3bandshort, lookup4bandshort;
+ private static final ConvolveOp
+ convolve3x3zero, convolve5x5zero, convolve7x7zero;
+ private static final ConvolveOp
+ convolve3x3noop, convolve5x5noop, convolve7x7noop;
+
+ static {
+ rescale1band = new RescaleOp(0.5f, 10.0f, null);
+ rescale3band = new RescaleOp(
+ new float[] { 0.6f, 0.4f, 0.6f },
+ new float[] { 10.0f, -3.0f, 5.0f },
+ null);
+ rescale4band = new RescaleOp(
+ new float[] { 0.6f, 0.4f, 0.6f, 0.9f },
+ new float[] { -1.0f, 5.0f, 3.0f, 1.0f },
+ null);
+
+ // REMIND: we should probably test non-zero offsets, but that
+ // would require massaging the source image data to avoid going
+ // outside the lookup table array bounds
+ int offset = 0;
+ {
+ byte invert[] = new byte[256];
+ byte halved[] = new byte[256];
+ for (int j = 0; j < 256 ; j++) {
+ invert[j] = (byte) (255-j);
+ halved[j] = (byte) (j / 2);
+ }
+ ByteLookupTable lut1 = new ByteLookupTable(offset, invert);
+ lookup1bandbyte = new LookupOp(lut1, null);
+ ByteLookupTable lut3 =
+ new ByteLookupTable(offset,
+ new byte[][] {invert, halved, invert});
+ lookup3bandbyte = new LookupOp(lut3, null);
+ ByteLookupTable lut4 =
+ new ByteLookupTable(offset,
+ new byte[][] {invert, halved, invert, halved});
+ lookup4bandbyte = new LookupOp(lut4, null);
+ }
+
+ {
+ short invert[] = new short[256];
+ short halved[] = new short[256];
+ for (int j = 0; j < 256 ; j++) {
+ invert[j] = (short) ((255-j) * 255);
+ halved[j] = (short) ((j / 2) * 255);
+ }
+ ShortLookupTable lut1 = new ShortLookupTable(offset, invert);
+ lookup1bandshort = new LookupOp(lut1, null);
+ ShortLookupTable lut3 =
+ new ShortLookupTable(offset,
+ new short[][] {invert, halved, invert});
+ lookup3bandshort = new LookupOp(lut3, null);
+ ShortLookupTable lut4 =
+ new ShortLookupTable(offset,
+ new short[][] {invert, halved, invert, halved});
+ lookup4bandshort = new LookupOp(lut4, null);
+ }
+
+ // 3x3 blur
+ float[] data3 = {
+ 0.1f, 0.1f, 0.1f,
+ 0.1f, 0.2f, 0.1f,
+ 0.1f, 0.1f, 0.1f,
+ };
+ Kernel k3 = new Kernel(3, 3, data3);
+
+ // 5x5 edge
+ float[] data5 = {
+ -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
+ -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
+ -1.0f, -1.0f, 24.0f, -1.0f, -1.0f,
+ -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
+ -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
+ };
+ Kernel k5 = new Kernel(5, 5, data5);
+
+ // 7x7 blur
+ float[] data7 = {
+ 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
+ 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
+ 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
+ 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
+ 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
+ 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
+ 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
+ };
+ Kernel k7 = new Kernel(7, 7, data7);
+
+ convolve3x3zero = new ConvolveOp(k3, ConvolveOp.EDGE_ZERO_FILL, null);
+ convolve5x5zero = new ConvolveOp(k5, ConvolveOp.EDGE_ZERO_FILL, null);
+ convolve7x7zero = new ConvolveOp(k7, ConvolveOp.EDGE_ZERO_FILL, null);
+
+ convolve3x3noop = new ConvolveOp(k3, ConvolveOp.EDGE_NO_OP, null);
+ convolve5x5noop = new ConvolveOp(k5, ConvolveOp.EDGE_NO_OP, null);
+ convolve7x7noop = new ConvolveOp(k7, ConvolveOp.EDGE_NO_OP, null);
+ }
+
+ public void paint(Graphics g) {
+
+ VolatileImage vimg = createVolatileImage(TESTW, TESTH);
+ vimg.validate(getGraphicsConfiguration());
+
+ Graphics2D g2d = vimg.createGraphics();
+ renderTest(g2d);
+ g2d.dispose();
+
+ g.drawImage(vimg, 0, 0, null);
+ }
+
+ /*
+ * foreach source image size (once with pow2, once with non-pow2)
+ *
+ * foreach BufferedImage type
+ *
+ * RescaleOp (1 band)
+ * RescaleOp (3 bands, if src has 3 bands)
+ * RescaleOp (4 bands, if src has 4 bands)
+ *
+ * foreach LookupTable type (once with ByteLUT, once with ShortLUT)
+ * LookupOp (1 band)
+ * LookupOp (3 bands, if src has 3 bands)
+ * LookupOp (4 bands, if src has 4 bands)
+ *
+ * foreach edge condition (once with ZERO_FILL, once with EDGE_NO_OP)
+ * ConvolveOp (3x3)
+ * ConvolveOp (5x5)
+ * ConvolveOp (7x7)
+ */
+ private void renderTest(Graphics2D g2d) {
+ g2d.setColor(Color.white);
+ g2d.fillRect(0, 0, TESTW, TESTH);
+
+ int yorig = 2;
+ int xinc = 34;
+ int yinc = srcSizes[0] + srcSizes[1] + 2 + 2;
+
+ for (int srcType : srcTypes) {
+ int y = yorig;
+
+ for (int srcSize : srcSizes) {
+ int x = 2;
+ System.out.printf("type=%d size=%d\n", srcType, srcSize);
+
+ BufferedImage srcImg = makeSourceImage(srcSize, srcType);
+ ColorModel srcCM = srcImg.getColorModel();
+
+ // RescaleOp
+ g2d.drawImage(srcImg, rescale1band, x, y);
+ x += xinc;
+ // REMIND: 3-band RescaleOp.filter() throws IAE for images
+ // that contain an alpha channel (bug to be filed)
+ if (srcCM.getNumColorComponents() == 3 &&
+ !(ignore && srcCM.hasAlpha()))
+ {
+ g2d.drawImage(srcImg, rescale3band, x, y);
+ }
+ x += xinc;
+ if (srcCM.getNumComponents() == 4) {
+ g2d.drawImage(srcImg, rescale4band, x, y);
+ }
+ x += xinc;
+
+ // LookupOp
+ // REMIND: Our LUTs are only 256 elements long, so won't
+ // currently work with USHORT_GRAY data
+ if (srcType != BufferedImage.TYPE_USHORT_GRAY) {
+ g2d.drawImage(srcImg, lookup1bandbyte, x, y);
+ x += xinc;
+ if (srcCM.getNumColorComponents() == 3) {
+ g2d.drawImage(srcImg, lookup3bandbyte, x, y);
+ }
+ x += xinc;
+ if (srcCM.getNumComponents() == 4) {
+ g2d.drawImage(srcImg, lookup4bandbyte, x, y);
+ }
+ x += xinc;
+
+ // REMIND: LookupOp.createCompatibleDestImage() throws
+ // IAE for 3BYTE_BGR/4BYTE_ABGR (bug to be filed)
+ if (!(ignore &&
+ (srcType == BufferedImage.TYPE_3BYTE_BGR ||
+ srcType == BufferedImage.TYPE_4BYTE_ABGR)))
+ {
+ g2d.drawImage(srcImg, lookup1bandshort, x, y);
+ x += xinc;
+ // REMIND: 3-band LookupOp.filter() throws IAE for
+ // images that contain an alpha channel
+ // (bug to be filed)
+ if (srcCM.getNumColorComponents() == 3 &&
+ !(ignore && srcCM.hasAlpha()))
+ {
+ g2d.drawImage(srcImg, lookup3bandshort, x, y);
+ }
+ x += xinc;
+ if (srcCM.getNumComponents() == 4) {
+ g2d.drawImage(srcImg, lookup4bandshort, x, y);
+ }
+ x += xinc;
+ } else {
+ x += 3*xinc;
+ }
+ } else {
+ x += 6*xinc;
+ }
+
+ // ConvolveOp
+ // REMIND: ConvolveOp.filter() throws ImagingOpException
+ // for 3BYTE_BGR (see 4957775)
+ if (srcType != BufferedImage.TYPE_3BYTE_BGR) {
+ g2d.drawImage(srcImg, convolve3x3zero, x, y);
+ x += xinc;
+ g2d.drawImage(srcImg, convolve5x5zero, x, y);
+ x += xinc;
+ g2d.drawImage(srcImg, convolve7x7zero, x, y);
+ x += xinc;
+
+ g2d.drawImage(srcImg, convolve3x3noop, x, y);
+ x += xinc;
+ g2d.drawImage(srcImg, convolve5x5noop, x, y);
+ x += xinc;
+ g2d.drawImage(srcImg, convolve7x7noop, x, y);
+ x += xinc;
+ } else {
+ x += 6*xinc;
+ }
+
+ y += srcSize + 2;
+ }
+
+ yorig += yinc;
+ }
+ }
+
+ private BufferedImage makeSourceImage(int size, int type) {
+ int s2 = size/2;
+ BufferedImage img = new BufferedImage(size, size, type);
+ Graphics2D g2d = img.createGraphics();
+ g2d.setComposite(AlphaComposite.Src);
+ g2d.setColor(Color.orange);
+ g2d.fillRect(0, 0, size, size);
+ g2d.setColor(Color.red);
+ g2d.fillRect(0, 0, s2, s2);
+ g2d.setColor(Color.green);
+ g2d.fillRect(s2, 0, s2, s2);
+ g2d.setColor(Color.blue);
+ g2d.fillRect(0, s2, s2, s2);
+ g2d.setColor(new Color(255, 255, 0, 128));
+ g2d.fillRect(s2, s2, s2, s2);
+ g2d.setColor(Color.pink);
+ g2d.fillOval(s2-3, s2-3, 6, 6);
+ g2d.dispose();
+ return img;
+ }
+
+ public BufferedImage makeReferenceImage() {
+ BufferedImage img = new BufferedImage(TESTW, TESTH,
+ BufferedImage.TYPE_INT_RGB);
+ Graphics2D g2d = img.createGraphics();
+ renderTest(g2d);
+ g2d.dispose();
+ return img;
+ }
+
+ public Dimension getPreferredSize() {
+ return new Dimension(TESTW, TESTH);
+ }
+
+ private static void compareImages(BufferedImage refImg,
+ BufferedImage testImg,
+ int tolerance)
+ {
+ int x1 = 0;
+ int y1 = 0;
+ int x2 = refImg.getWidth();
+ int y2 = refImg.getHeight();
+
+ for (int y = y1; y < y2; y++) {
+ for (int x = x1; x < x2; x++) {
+ Color expected = new Color(refImg.getRGB(x, y));
+ Color actual = new Color(testImg.getRGB(x, y));
+ if (!isSameColor(expected, actual, tolerance)) {
+ saveImage("referenceimage", refImg);
+ saveImage("testimage", testImg);
+ throw new RuntimeException("Test failed at x="+x+" y="+y+
+ " (expected="+expected+
+ " actual="+actual+
+ ")");
+ }
+ }
+ }
+ }
+
+ private static boolean isSameColor(Color c1, Color c2, int e) {
+ int r1 = c1.getRed();
+ int g1 = c1.getGreen();
+ int b1 = c1.getBlue();
+ int r2 = c2.getRed();
+ int g2 = c2.getGreen();
+ int b2 = c2.getBlue();
+ int rmin = Math.max(r2-e, 0);
+ int gmin = Math.max(g2-e, 0);
+ int bmin = Math.max(b2-e, 0);
+ int rmax = Math.min(r2+e, 255);
+ int gmax = Math.min(g2+e, 255);
+ int bmax = Math.min(b2+e, 255);
+ if (r1 >= rmin && r1 <= rmax &&
+ g1 >= gmin && g1 <= gmax &&
+ b1 >= bmin && b1 <= bmax)
+ {
+ return true;
+ }
+ return false;
+ }
+
+
+ static void createUI() {
+ test = new DrawBufImgOp();
+ Panel panel = new Panel();
+ panel.add(test);
+ frame = new Frame("OpenGL DrawBufImgOp Test");
+ frame.add(panel);
+ frame.setSize(TESTW+100, TESTH+100);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ boolean show = false;
+ boolean dump = false;
+ boolean compare = false;
+
+ for (String arg : args) {
+ if (arg.equals("-show")) {
+ show = true;
+ } else if (arg.equals("-dump")) {
+ dump = true;
+ } else if (arg.equals("-compare")) {
+ compare = true;
+ } else if (arg.equals("-ignore")) {
+ ignore = true;
+ }
+ }
+
+ Robot robot = new Robot();
+
+ EventQueue.invokeAndWait(DrawBufImgOp::createUI);
+
+ robot.waitForIdle();
+ robot.delay(2000);
+
+ BufferedImage capture = null;
+ try {
+ GraphicsConfiguration gc = frame.getGraphicsConfiguration();
+ if (gc.getColorModel() instanceof IndexColorModel) {
+ System.out.println("IndexColorModel detected: " +
+ "test considered PASSED");
+ return;
+ }
+ Point pt1 = test.getLocationOnScreen();
+ Rectangle rect = new Rectangle(pt1.x, pt1.y, TESTW, TESTH);
+ capture = robot.createScreenCapture(rect);
+ } finally {
+ if (frame != null) {
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+ }
+
+ // Compare the images (allow for +/- 1 bit differences in color comps)
+ if (dump || compare) {
+ BufferedImage ref = test.makeReferenceImage();
+ if (dump) {
+ saveImage("DrawBufImgOp_ref", ref);
+ saveImage("DrawBufImgOp_cap", capture);
+ }
+ if (compare) {
+ test.compareImages(ref, capture, 1);
+ }
+ }
+ }
+
+ static void saveImage(String name, BufferedImage img) {
+ try {
+ File file = new File(name + ".png");
+ ImageIO.write(img, "png", file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/test/jdk/sun/java2d/OpenGL/DrawImageBg.java b/test/jdk/sun/java2d/OpenGL/DrawImageBg.java
new file mode 100644
index 00000000000..7fc38d91b06
--- /dev/null
+++ b/test/jdk/sun/java2d/OpenGL/DrawImageBg.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2004, 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 4993274
+ * @key headful
+ * @requires (os.family != "mac")
+ * @summary Verifies that managed image copies and transforms work properly
+ * with the OGL pipeline when a background color is specified.
+ * @run main/othervm -Dsun.java2d.opengl=True DrawImageBg
+ * @run main/othervm DrawImageBg
+ */
+
+/*
+ * @test
+ * @bug 4993274
+ * @key headful
+ * @requires (os.family == "mac")
+ * @summary Verifies that managed image copies and transforms work properly
+ * with the OGL pipeline when a background color is specified.
+ * @run main/othervm -Dsun.java2d.opengl=True DrawImageBg
+ * @run main/othervm DrawImageBg
+ */
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+public class DrawImageBg extends Panel {
+
+ static volatile Frame frame;
+ static volatile DrawImageBg test;
+
+ public void paint(Graphics g) {
+
+ Graphics2D g2d = (Graphics2D)g;
+ g2d.setColor(Color.black);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+
+ BufferedImage img = getGraphicsConfiguration().createCompatibleImage(50, 50,
+ Transparency.BITMASK);
+ Graphics2D gimg = img.createGraphics();
+ gimg.setComposite(AlphaComposite.Src);
+ gimg.setColor(new Color(0, 0, 0, 0));
+ gimg.fillRect(0, 0, 50, 50);
+ gimg.setColor(Color.red);
+ gimg.fillRect(10, 10, 30, 30);
+ gimg.dispose();
+
+ g2d.drawImage(img, 10, 10, Color.blue, null);
+
+ // draw a second time to ensure that the cached copy is used
+ g2d.drawImage(img, 80, 10, Color.blue, null);
+ }
+
+ static void createUI() {
+ frame = new Frame("OpenGL DrawImageBg Test");
+ test = new DrawImageBg();
+ frame.add(test);
+ frame.setSize(300, 300);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ BufferedImage capture = null;
+ Robot robot = new Robot();
+ try {
+ EventQueue.invokeAndWait(DrawImageBg::createUI);
+ robot.waitForIdle();
+ robot.delay(3000);
+
+ // Grab the screen region
+ Point pt1 = test.getLocationOnScreen();
+ Rectangle rect = new Rectangle(pt1.x+80, pt1.y, 80, 80);
+ capture = robot.createScreenCapture(rect);
+ } finally {
+ if (frame != null) {
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+ }
+
+ if (capture == null) {
+ throw new RuntimeException("Screen capture is null");
+ }
+
+ // Test inner and outer pixels
+ int pixel1 = capture.getRGB(5, 10);
+ if (pixel1 != 0xff0000ff) {
+ saveImage(capture);
+ throw new RuntimeException(getMsg("outer", pixel1));
+ }
+ int pixel2 = capture.getRGB(25, 25);
+ if (pixel2 != 0xffff0000) {
+ saveImage(capture);
+ throw new RuntimeException(getMsg("inner", pixel2));
+ }
+ }
+
+ static String getMsg(String r, int p1) {
+ return "Failed: Incorrect color for " + r + " pixel: got " + Integer.toHexString(p1);
+ }
+
+ static void saveImage(BufferedImage img) {
+ try {
+ File file = new File("capture.png");
+ ImageIO.write(img, "png", file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/test/jdk/sun/java2d/OpenGL/LargeOps.java b/test/jdk/sun/java2d/OpenGL/LargeOps.java
new file mode 100644
index 00000000000..ace60b7a3c4
--- /dev/null
+++ b/test/jdk/sun/java2d/OpenGL/LargeOps.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2004, 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 6219284 6358147 6274813 6578452
+ * @key headful
+ * @summary Verifies that OGLRenderer.drawPoly(),
+ * OGLTextRenderer.drawGlyphList(), and OGLMaskFill work properly when the
+ * operation parameters exceed the capacity of the render queue. With the
+ * single-threaded OpenGL pipeline, there are some operations that require
+ * a separate buffer to be spawned if the parameters cannot fit entirely on
+ * the standard buffer. This test exercises this special case.
+ * @run main/othervm -Dsun.java2d.opengl=True -Dsun.java2d.opengl.lcdshader=true LargeOps
+ */
+
+import java.awt.Canvas;
+import java.awt.Color;
+import java.awt.EventQueue;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Robot;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+public class LargeOps extends Canvas {
+
+ private static final int NUM_POINTS = 8000;
+ private int[] xPoints, yPoints;
+ private String str;
+
+ public LargeOps() {
+ xPoints = new int[NUM_POINTS];
+ yPoints = new int[NUM_POINTS];
+ for (int i = 0; i < NUM_POINTS; i++) {
+ xPoints[i] = (i % 2 == 0) ? 10 : 400;
+ yPoints[i] = (i % 2 == 1) ? i+3 : i;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < NUM_POINTS; i+=11) {
+ sb.append("ThisIsATest");
+ }
+ str = sb.toString();
+ }
+
+ public void paint(Graphics g) {
+ Graphics2D g2d = (Graphics2D)g;
+ g2d.setColor(Color.white);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+
+ // draw large polyline
+ g2d.setColor(Color.green);
+ g2d.drawPolyline(xPoints, yPoints, NUM_POINTS);
+
+ // draw long string
+ g2d.setColor(Color.blue);
+ g2d.drawString(str, 10, 100);
+
+ // draw long string with larger pt size
+ Font font = g2d.getFont();
+ g2d.setFont(font.deriveFont(40.0f));
+ g2d.drawString(str, 10, 150);
+
+ // do the same with LCD hints enabled
+ g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+ RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
+ g2d.setFont(font);
+ g2d.drawString(str, 10, 200);
+ g2d.setFont(font.deriveFont(43.0f));
+ g2d.drawString(str, 10, 250);
+
+ g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+ RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR);
+ g2d.setFont(font);
+ g2d.drawString(str, 10, 300);
+ g2d.setFont(font.deriveFont(37.0f));
+ g2d.drawString(str, 10, 350);
+ }
+
+ static volatile Frame frame;
+ static volatile LargeOps test;
+
+ static void createUI() {
+ frame = new Frame("OpenGL LargeOps Test");
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ frame.dispose();
+ }
+ });
+ test = new LargeOps();
+ frame.add(test);
+ frame.setSize(600, 600);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+ try {
+ Robot robot = new Robot();
+ EventQueue.invokeAndWait(LargeOps::createUI);
+ robot.waitForIdle();
+ robot.delay(6000);
+ } finally {
+ if (frame != null) {
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+ }
+ }
+}
diff --git a/test/jdk/sun/java2d/OpenGL/OpaqueDest.java b/test/jdk/sun/java2d/OpenGL/OpaqueDest.java
new file mode 100644
index 00000000000..50896316109
--- /dev/null
+++ b/test/jdk/sun/java2d/OpenGL/OpaqueDest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2004, 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 6277977 6319663
+ * @key headful
+ * @requires (os.family != "mac")
+ * @summary Verifies that blending operations do not inadvertantly leave
+ * non-opaque alpha values in the framebuffer. Note that this test is
+ * intended to run on GraphicsConfigs that support a stored alpha channel
+ * (to verify the bug at hand), but it is also a useful for testing the
+ * compositing results on any configuration.
+ * @run main/othervm -Dsun.java2d.opengl=True OpaqueDest
+ * @run main/othervm OpaqueDest
+ */
+
+/*
+ * @test
+ * @bug 6277977 6319663
+ * @key headful
+ * @requires (os.family == "mac")
+ * @summary Verifies that blending operations do not inadvertantly leave
+ * non-opaque alpha values in the framebuffer. Note that this test is
+ * intended to run on GraphicsConfigs that support a stored alpha channel
+ * (to verify the bug at hand), but it is also a useful for testing the
+ * compositing results on any configuration.
+ * @run main/othervm -Dsun.java2d.opengl=True OpaqueDest
+ * @run main/othervm OpaqueDest
+ */
+
+import java.awt.AlphaComposite;
+import java.awt.Canvas;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.image.BufferedImage;
+import java.awt.image.IndexColorModel;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+public class OpaqueDest extends Canvas {
+
+ private static volatile Frame frame;
+ private static volatile OpaqueDest test;
+
+ public void paint(Graphics g) {
+
+ Graphics2D g2d = (Graphics2D)g;
+
+ g2d.setColor(Color.red);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+
+ // This will clear the rectangle to black
+ g2d.setComposite(AlphaComposite.Clear);
+ g2d.fillRect(10, 10, 80, 80);
+
+ // If everything is working properly, then this will fill the
+ // rectangle with red again. Before this bug was fixed, the previous
+ // Clear operation would leave zero values in the destination's
+ // alpha channel (if present), and therefore a SrcIn operation
+ // would result in all-black.
+ g2d.setComposite(AlphaComposite.SrcIn);
+ g2d.fillRect(10, 10, 80, 80);
+ }
+
+ public Dimension getPreferredSize() {
+ return new Dimension(100, 100);
+ }
+
+ static void createUI() {
+ test = new OpaqueDest();
+ frame = new Frame("OpenGL OpaqueDest Test");
+ Panel p = new Panel();
+ p.add(test);
+ frame.add(p);
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+ Robot robot = new Robot();
+
+ EventQueue.invokeAndWait(OpaqueDest::createUI);
+
+ robot.waitForIdle();
+ robot.delay(2000);
+
+ BufferedImage capture = null;
+ try {
+ GraphicsConfiguration gc = frame.getGraphicsConfiguration();
+ if (gc.getColorModel() instanceof IndexColorModel) {
+ System.out.println("IndexColorModel detected: " +
+ "test considered PASSED");
+ return;
+ }
+ Point pt1 = test.getLocationOnScreen();
+ Rectangle rect = new Rectangle(pt1.x, pt1.y, 100, 100);
+ capture = robot.createScreenCapture(rect);
+ } finally {
+ if (frame != null) {
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+ }
+
+
+ // Test all pixels (every one should be red)
+ for (int y = 0; y < 100; y++) {
+ for (int x = 0; x < 100; x++) {
+ int actual = capture.getRGB(x, y);
+ int expected = 0xffff0000;
+ if (!similar(actual, expected)) {
+ saveImage(capture);
+ throw new RuntimeException("Test failed at x="+x+" y="+y+
+ " (expected="+
+ Integer.toHexString(expected) +
+ " actual="+
+ Integer.toHexString(actual) +
+ ")");
+ }
+ }
+ }
+ }
+
+ static boolean similar(int p1, int p2) {
+ int a1 = (p1 >> 24) & 0xff;
+ int r1 = (p1 >> 16) & 0xff;
+ int g1 = (p1 >> 8) & 0xff;
+ int b1 = p1 & 0xff;
+ int a2 = (p2 >> 24) & 0xff;
+ int r2 = (p2 >> 16) & 0xff;
+ int g2 = (p2 >> 8) & 0xff;
+ int b2 = p2 & 0xff;
+
+ int allowedDiff = 0x01; // tiny rounding error allowed.
+ return
+ (Math.abs(a1 - a2) <= allowedDiff) &&
+ (Math.abs(r1 - r2) <= allowedDiff) &&
+ (Math.abs(g1 - g2) <= allowedDiff) &&
+ (Math.abs(b1 - b2) <= allowedDiff);
+ }
+
+ static void saveImage(BufferedImage img) {
+ try {
+ File file = new File("capture.png");
+ ImageIO.write(img, "png", file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/test/jdk/sun/java2d/OpenGL/ScaleParamsOOB.java b/test/jdk/sun/java2d/OpenGL/ScaleParamsOOB.java
new file mode 100644
index 00000000000..b3d866cfd75
--- /dev/null
+++ b/test/jdk/sun/java2d/OpenGL/ScaleParamsOOB.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2004, 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 5104584 8237244
+ * @key headful
+ * @requires (os.family != "mac")
+ * @summary Verifies that scaling an image works properly when the
+ * source parameters are outside the source bounds.
+ * @run main/othervm -Dsun.java2d.opengl=True ScaleParamsOOB
+ * @run main/othervm ScaleParamsOOB
+ */
+
+/*
+ * @test
+ * @bug 5104584 8237244
+ * @key headful
+ * @requires (os.family == "mac")
+ * @summary Verifies that scaling an image works properly when the
+ * source parameters are outside the source bounds.
+ * @run main/othervm -Dsun.java2d.opengl=True ScaleParamsOOB
+ * @run main/othervm ScaleParamsOOB
+ */
+
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+public class ScaleParamsOOB extends Panel {
+
+ private static final int TOLERANCE = 12;
+
+ private static volatile ScaleParamsOOB test;
+ private static volatile Frame frame;
+
+ private BufferedImage img;
+
+ public void paint(Graphics g) {
+
+ Graphics2D g2d = (Graphics2D)g;
+ g2d.setColor(Color.black);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+
+ BufferedImage img = getGraphicsConfiguration().createCompatibleImage(40, 40);
+ Graphics2D gimg = img.createGraphics();
+ gimg.setColor(Color.red);
+ gimg.fillRect(0, 0, 40, 40);
+ gimg.dispose();
+
+ // first time will be a sw->surface blit
+ g2d.drawImage(img,
+ 10, 10, 90, 90,
+ -60, -60, 100, 100,
+ null);
+
+ // second time will be a texture->surface blit
+ g2d.drawImage(img,
+ 110, 10, 190, 90,
+ -60, -60, 100, 100,
+ null);
+ }
+
+ public Dimension getPreferredSize() {
+ return new Dimension(300, 200);
+ }
+
+ private static void testRegion(BufferedImage bi,
+ Rectangle wholeRegion,
+ Rectangle affectedRegion)
+ {
+ int x1 = wholeRegion.x;
+ int y1 = wholeRegion.y;
+ int x2 = x1 + wholeRegion.width;
+ int y2 = y1 + wholeRegion.height;
+
+ for (int y = y1; y < y2; y++) {
+ for (int x = x1; x < x2; x++) {
+ int actual = bi.getRGB(x, y);
+ int expected = 0;
+ if (affectedRegion.contains(x, y)) {
+ expected = Color.red.getRGB();
+ } else {
+ expected = Color.black.getRGB();
+ }
+ int alpha = (actual >> 24) & 0xFF;
+ int red = (actual >> 16) & 0xFF;
+ int green = (actual >> 8) & 0xFF;
+ int blue = (actual) & 0xFF;
+
+ int standardAlpha = (expected >> 24) & 0xFF;
+ int standardRed = (expected >> 16) & 0xFF;
+ int standardGreen = (expected >> 8) & 0xFF;
+ int standardBlue = (expected) & 0xFF;
+
+ if ((Math.abs(alpha - standardAlpha) > TOLERANCE) ||
+ (Math.abs(red - standardRed) > TOLERANCE) ||
+ (Math.abs(green - standardGreen) > TOLERANCE) ||
+ (Math.abs(blue - standardBlue) > TOLERANCE)) {
+ saveImage(bi);
+ throw new RuntimeException("Test failed at x="+x+" y="+y+
+ " (expected="+
+ Integer.toHexString(expected) +
+ " actual="+
+ Integer.toHexString(actual) +
+ ")");
+ }
+ }
+ }
+ }
+
+ private static void createAndShowGUI() {
+ test = new ScaleParamsOOB();
+ frame = new Frame("OpenGL ScaleParamsOOB Test");
+ frame.setAlwaysOnTop(true);
+ frame.add(test);
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+ Robot robot = new Robot();
+
+ EventQueue.invokeAndWait(() -> createAndShowGUI());
+
+ robot.waitForIdle();
+ robot.delay(2000);
+
+ // Grab the screen region
+ BufferedImage capture = null;
+ try {
+ Point pt1 = test.getLocationOnScreen();
+ Rectangle rect = new Rectangle(pt1.x, pt1.y, 200, 200);
+ capture = robot.createScreenCapture(rect);
+ } finally {
+ if (frame != null) {
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+ }
+
+ // Test background color
+ int pixel = capture.getRGB(5, 5);
+ if (pixel != 0xff000000) {
+ saveImage(capture);
+ throw new RuntimeException("Failed: Incorrect color for " +
+ "background: " + Integer.toHexString(pixel));
+ }
+
+ // Test pixels
+ testRegion(capture,
+ new Rectangle(5, 5, 90, 90),
+ new Rectangle(40, 40, 20, 20));
+ testRegion(capture,
+ new Rectangle(105, 5, 90, 90),
+ new Rectangle(140, 40, 20, 20));
+ }
+
+ static void saveImage(BufferedImage img) {
+ try {
+ File file = new File("capture.png");
+ ImageIO.write(img, "png", file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/test/jdk/sun/java2d/OpenGL/ShapeClip.java b/test/jdk/sun/java2d/OpenGL/ShapeClip.java
new file mode 100644
index 00000000000..f50b7aff5a6
--- /dev/null
+++ b/test/jdk/sun/java2d/OpenGL/ShapeClip.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2004, 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 5002133
+ * @key headful
+ * @requires (os.family != "mac")
+ * @summary Verifies that the OpenGL pipeline does not affect the color
+ * buffer when setting up a complex (shape) clip region. The test fails if
+ * the circular clip region is filled with a green color (the green region
+ * should not be visible at all).
+ * @run main/othervm -Dsun.java2d.opengl=True ShapeClip
+ * @run main/othervm ShapeClip
+ */
+
+/*
+ * @test
+ * @bug 5002133
+ * @key headful
+ * @requires (os.family == "mac")
+ * @summary Verifies that the OpenGL pipeline does not affect the color
+ * buffer when setting up a complex (shape) clip region. The test fails if
+ * the circular clip region is filled with a green color (the green region
+ * should not be visible at all).
+ * @run main/othervm -Dsun.java2d.opengl=True ShapeClip
+ * @run main/othervm ShapeClip
+ */
+
+import java.awt.Color;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.geom.Ellipse2D;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+public class ShapeClip extends Panel {
+
+ private static volatile Frame frame;
+ private static volatile ShapeClip test;
+
+ public void paint(Graphics g) {
+
+ Graphics2D g2d = (Graphics2D)g;
+
+ int width = getWidth();
+ int height = getHeight();
+
+ g2d.setColor(Color.black);
+ g2d.fillRect(0, 0, width, height);
+
+ g2d.setColor(Color.green);
+ g2d.fillRect(0, 0, 1, 1);
+ g2d.setClip(new Ellipse2D.Double(10, 10, 100, 100));
+ g2d.setColor(Color.blue);
+ g2d.fillRect(30, 30, 20, 20);
+ }
+
+ static void createUI() {
+ test = new ShapeClip();
+ frame = new Frame("OpenGL ShapeClip Test");
+ frame.add(test);
+ frame.setSize(200, 200);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+ Robot robot = new Robot();
+
+ EventQueue.invokeAndWait(ShapeClip::createUI);
+
+ robot.waitForIdle();
+ robot.delay(2000);
+
+ // Grab the screen region
+ BufferedImage capture = null;
+ try {
+ Point pt1 = test.getLocationOnScreen();
+ Rectangle rect = new Rectangle(pt1.x, pt1.y, 80, 80);
+ capture = robot.createScreenCapture(rect);
+ } finally {
+ if (frame != null) {
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+ }
+
+ // Test blue rectangle
+ int pixel1 = capture.getRGB(40, 40);
+ if (pixel1 != 0xff0000ff) {
+ saveImage(capture);
+ throw new RuntimeException("Failed: Incorrect color for " +
+ "rectangle " + Integer.toHexString(pixel1));
+ }
+
+ // Test clip region (should be same color as background)
+ int pixel2 = capture.getRGB(60, 40);
+ if (pixel2 != 0xff000000) {
+ saveImage(capture);
+ throw new RuntimeException("Failed: Incorrect color for " +
+ "clip region " + Integer.toHexString(pixel2));
+ }
+ }
+
+ static void saveImage(BufferedImage img) {
+ try {
+ File file = new File("capture.png");
+ ImageIO.write(img, "png", file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/test/jdk/sun/java2d/OpenGL/SrcMaskOps.java b/test/jdk/sun/java2d/OpenGL/SrcMaskOps.java
new file mode 100644
index 00000000000..9908cffdefb
--- /dev/null
+++ b/test/jdk/sun/java2d/OpenGL/SrcMaskOps.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2004, 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 4942939 4970674
+ * @key headful
+ * @requires (os.family != "mac")
+ * @summary Verifies that OGLMaskFill, OGLMaskBlit, and OGLTextRenderer
+ * operations work properly for non-SrcOver composites.
+ * @run main/othervm -Dsun.java2d.opengl=True SrcMaskOps
+ * @run main/othervm SrcMaskOps
+ */
+
+/*
+ * @test
+ * @bug 4942939 4970674
+ * @key headful
+ * @requires (os.family == "mac")
+ * @summary Verifies that OGLMaskFill, OGLMaskBlit, and OGLTextRenderer
+ * operations work properly for non-SrcOver composites.
+ * @run main/othervm -Dsun.java2d.opengl=True SrcMaskOps
+ * @run main/othervm SrcMaskOps
+ */
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.EventQueue;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.GradientPaint;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Robot;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+public class SrcMaskOps extends Panel {
+
+ static volatile Frame frame;
+ static volatile SrcMaskOps test;
+
+ static final int SRX = 50;
+ static final int SRY = 50;
+ static final int GPX = 90;
+ static final int GPY = 50;
+ static final int DTX = 120;
+ static final int DTY = 70;
+
+ public void paint(Graphics g) {
+
+ Graphics2D g2d = (Graphics2D)g;
+
+ g2d.setColor(Color.white);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ g2d.setComposite(AlphaComposite.Src);
+
+ g2d.setColor(Color.blue);
+ g2d.drawRect(SRX, SRY, 20, 20);
+
+ g2d.setPaint(new GradientPaint(0.0f, 0.0f, Color.red,
+ 100.0f, 100.f, Color.red, true));
+ g2d.drawRect(GPX, GPY, 20, 20);
+
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_OFF);
+
+ g2d.setColor(Color.red);
+ Font font = new Font(Font.DIALOG, Font.PLAIN, 20);
+ g2d.setFont(font);
+ g2d.drawString("HELLO", DTX, DTY);
+ }
+
+ static void createUI() {
+ frame = new Frame("OpenGL SrcMaskOps Test");
+ test = new SrcMaskOps();
+ frame.add(test);
+ frame.setSize(300, 300);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ Robot robot = new Robot();
+ BufferedImage capture = null;
+ try {
+ EventQueue.invokeAndWait(SrcMaskOps::createUI);
+ robot.waitForIdle();
+ robot.delay(3000);
+
+ // Grab the screen region
+ Point pt1 = test.getLocationOnScreen();
+ Rectangle rect = new Rectangle(pt1.x, pt1.y, 300, 300);
+ capture = robot.createScreenCapture(rect);
+ } finally {
+ if (frame != null) {
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+ }
+
+ // Test solid rectangle
+ int pixel1, pixel2;
+ pixel1 = capture.getRGB(SRX, SRY);
+ pixel2 = capture.getRGB(SRX+2, SRY+2);
+ if (!similar(pixel1, 0xff0000ff) || !similar(pixel2, 0xffffffff)) {
+ saveImage(capture);
+ throw new RuntimeException(getMsg("solid rectangle", pixel1, pixel2));
+ }
+
+ // Test GradientPaint rectangle
+ pixel1 = capture.getRGB(GPX, GPY);
+ pixel2 = capture.getRGB(GPX+2, GPY+2);
+ if (!similar(pixel1, 0xffff0000) || !similar(pixel2, 0xffffffff)) {
+ saveImage(capture);
+ throw new RuntimeException(getMsg("GradientPaint rectangle", pixel1, pixel2));
+ }
+
+ // Test solid text
+ pixel1 = capture.getRGB(DTX+2, DTY-5);
+ pixel2 = capture.getRGB(DTX+5, DTY-5);
+ if (!similar(pixel1, 0xffff0000) || !similar(pixel2, 0xffffffff)) {
+ saveImage(capture);
+ throw new RuntimeException(getMsg("solid text", pixel1, pixel2));
+ }
+
+ }
+
+ static boolean similar(int p1, int p2) {
+ int a1 = (p1 >> 24) & 0xff;
+ int r1 = (p1 >> 16) & 0xff;
+ int g1 = (p1 >> 8) & 0xff;
+ int b1 = p1 & 0xff;
+ int a2 = (p2 >> 24) & 0xff;
+ int r2 = (p2 >> 16) & 0xff;
+ int g2 = (p2 >> 8) & 0xff;
+ int b2 = p2 & 0xff;
+
+ int allowedDiff = 0x10;
+ return
+ (Math.abs(a1 - a2) <= allowedDiff) &&
+ (Math.abs(r1 - r2) <= allowedDiff) &&
+ (Math.abs(g1 - g2) <= allowedDiff) &&
+ (Math.abs(b1 - b2) <= allowedDiff);
+ }
+
+ static String getMsg(String r, int p1, int p2) {
+ return "Failed: Incorrect color[s] for " + r + " got " +
+ Integer.toHexString(p1) + " and " + Integer.toHexString(p2);
+ }
+
+ static void saveImage(BufferedImage img) {
+ try {
+ File file = new File("capture.png");
+ ImageIO.write(img, "png", file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/test/jdk/sun/java2d/OpenGL/VolatileSubRegion.java b/test/jdk/sun/java2d/OpenGL/VolatileSubRegion.java
new file mode 100644
index 00000000000..7ec350bc958
--- /dev/null
+++ b/test/jdk/sun/java2d/OpenGL/VolatileSubRegion.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2004, 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 6244071
+ * @key headful
+ * @requires (os.family != "mac")
+ * @summary Verifies that copying a subregion from a VolatileImage works
+ * properly with the OGL pipeline.
+ * @run main/othervm VolatileSubRegion
+ * @run main/othervm -Dsun.java2d.opengl=True -Dsun.java2d.opengl.fbobject=true VolatileSubRegion
+ * @run main/othervm -Dsun.java2d.opengl=True -Dsun.java2d.opengl.fbobject=false VolatileSubRegion
+ */
+
+/*
+ * @test
+ * @bug 6244071
+ * @key headful
+ * @requires (os.family == "mac")
+ * @summary Verifies that copying a subregion from a VolatileImage works
+ * properly with the OGL pipeline.
+ * @run main/othervm VolatileSubRegion
+ * @run main/othervm -Dsun.java2d.opengl=True -Dsun.java2d.opengl.fbobject=true VolatileSubRegion
+ * @run main/othervm -Dsun.java2d.opengl=True -Dsun.java2d.opengl.fbobject=false VolatileSubRegion
+ */
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.image.BufferedImage;
+import java.awt.image.IndexColorModel;
+import java.awt.image.VolatileImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+public class VolatileSubRegion extends Panel {
+
+ private VolatileImage img;
+
+ public void paint(Graphics g) {
+
+ Graphics2D g2d = (Graphics2D)g;
+
+ if (img == null) {
+ img = createVolatileImage(200, 200);
+ Graphics2D goff = img.createGraphics();
+ goff.setColor(Color.green);
+ goff.fillRect(50, 0, 100, 50);
+ goff.setColor(Color.blue);
+ goff.fillRect(0, 0, 200, 200);
+ goff.setColor(Color.red);
+ goff.fillRect(50, 50, 100, 100);
+ goff.setColor(Color.yellow);
+ goff.fillRect(50, 150, 100, 50);
+ goff.dispose();
+ }
+
+ g2d.setColor(Color.white);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+
+ g2d.drawImage(img,
+ 50, 50, 200, 200,
+ 50, 50, 200, 200,
+ null);
+
+ }
+
+
+ private static volatile VolatileSubRegion test;
+ private static volatile Frame frame;
+
+ static void createUI() {
+ test = new VolatileSubRegion();
+ frame = new Frame("OpenGL VolatileSubRegion Test");
+ frame.add(test);
+ frame.setSize(300, 300);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+ Robot robot = new Robot();
+
+ EventQueue.invokeAndWait(VolatileSubRegion::createUI);
+
+ robot.waitForIdle();
+ robot.delay(2000);
+
+ BufferedImage capture = null;
+ try {
+ GraphicsConfiguration gc = frame.getGraphicsConfiguration();
+ if (gc.getColorModel() instanceof IndexColorModel) {
+ System.out.println("IndexColorModel detected: " +
+ "test considered PASSED");
+ return;
+ }
+ Point pt1 = test.getLocationOnScreen();
+ Rectangle rect = new Rectangle(pt1.x, pt1.y, 200, 200);
+ capture = robot.createScreenCapture(rect);
+ } finally {
+ if (frame != null) {
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+ }
+
+ // Test pixels
+ int pixel1 = capture.getRGB(49, 50);
+ if (pixel1 != 0xffffffff) {
+ saveImage(capture);
+ throw new RuntimeException(getMsg("background pixel", pixel1));
+ }
+ int pixel2 = capture.getRGB(50, 50);
+ if (pixel2 != 0xffff0000) {
+ saveImage(capture);
+ throw new RuntimeException(getMsg("red region", pixel2));
+ }
+ int pixel3 = capture.getRGB(50, 150);
+ if (pixel3 != 0xffffff00) {
+ saveImage(capture);
+ throw new RuntimeException(getMsg("yellow region", pixel3));
+ }
+ }
+
+ static String getMsg(String r, int p1) {
+ return "Failed: Incorrect color for " + r + " : got " + Integer.toHexString(p1);
+ }
+
+ static void saveImage(BufferedImage img) {
+ try {
+ File file = new File("capture.png");
+ ImageIO.write(img, "png", file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/test/jdk/sun/java2d/OpenGL/XformVolatile.java b/test/jdk/sun/java2d/OpenGL/XformVolatile.java
new file mode 100644
index 00000000000..44e7c7ee8ba
--- /dev/null
+++ b/test/jdk/sun/java2d/OpenGL/XformVolatile.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2004, 2025, Oracle and/or its 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 4970836
+ * @key headful
+ * @requires (os.family != "mac")
+ * @summary Verifies that transformed VolatileImage copies work properly with
+ * the OGL pipeline.
+ * @run main/othervm XformVolatile
+ * @run main/othervm -Dsun.java2d.opengl=True -Dsun.java2d.opengl.fbobject=true XformVolatile
+ * @run main/othervm -Dsun.java2d.opengl=True -Dsun.java2d.opengl.fbobject=false XformVolatile
+ */
+
+/*
+ * @test
+ * @bug 4970836
+ * @key headful
+ * @requires (os.family == "mac")
+ * @summary Verifies that transformed VolatileImage copies work properly with
+ * the OGL pipeline.
+ * @run main/othervm XformVolatile
+ * @run main/othervm -Dsun.java2d.opengl=True -Dsun.java2d.opengl.fbobject=true XformVolatile
+ * @run main/othervm -Dsun.java2d.opengl=True -Dsun.java2d.opengl.fbobject=false XformVolatile
+ */
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+public class XformVolatile extends Panel {
+
+ private static volatile Frame frame;
+ private static volatile XformVolatile test;
+ private volatile VolatileImage img;
+
+ public void paint(Graphics g) {
+
+ Graphics2D g2d = (Graphics2D)g;
+
+ if (img == null) {
+ img = createVolatileImage(200, 200);
+ Graphics2D goff = img.createGraphics();
+ goff.setColor(Color.blue);
+ goff.fillRect(0, 0, 200, 200);
+ goff.setColor(Color.red);
+ goff.fillPolygon(new int[] {10, 100, 190},
+ new int[] {190, 10, 190}, 3);
+ goff.dispose();
+ }
+
+ g2d.setColor(Color.black);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+
+ g2d.rotate(Math.toRadians(3.0));
+ g2d.drawImage(img, 0, 0, null);
+ }
+
+ static void createUI() {
+ test = new XformVolatile();
+ frame = new Frame("OpenGL XformVolatile Test");
+ frame.add(test);
+ frame.setSize(300, 300);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ Robot robot = new Robot();
+
+ EventQueue.invokeAndWait(XformVolatile::createUI);
+
+ robot.waitForIdle();
+ robot.delay(2000);
+
+ // Grab the screen region
+ BufferedImage capture = null;
+ try {
+ Point pt1 = test.getLocationOnScreen();
+ Rectangle rect = new Rectangle(pt1.x, pt1.y, 200, 200);
+ capture = robot.createScreenCapture(rect);
+ } finally {
+ if (frame != null) {
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+ }
+
+ // Test inner and outer pixels
+ int pixel1 = capture.getRGB(5, 175);
+ if (pixel1 != 0xff0000ff) {
+ saveImage(capture);
+ throw new RuntimeException(getMsg("inner", pixel1));
+ }
+ int pixel2 = capture.getRGB(5, 188);
+ if (pixel2 != 0xffff0000) {
+ saveImage(capture);
+ throw new RuntimeException(getMsg("inner", pixel2));
+ }
+ }
+
+ static String getMsg(String r, int p1) {
+ return "Failed: Incorrect color for " + r + " pixel: got " + Integer.toHexString(p1);
+ }
+
+ static void saveImage(BufferedImage img) {
+ try {
+ File file = new File("capture.png");
+ ImageIO.write(img, "png", file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
From 1ea8cfa6dc8e6f96fd87553331abaae17ec173ea Mon Sep 17 00:00:00 2001
From: Aleksey Shipilev
Date: Tue, 7 Oct 2025 16:54:36 +0000
Subject: [PATCH 052/160] 8369226: GHA: Switch to MacOS 15
Reviewed-by: erikj, ayang, sgehwolf
---
.github/workflows/main.yml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0d8663fab1a..4d1e8a8be3d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -327,8 +327,8 @@ jobs:
uses: ./.github/workflows/build-macos.yml
with:
platform: macos-x64
- runs-on: 'macos-13'
- xcode-toolset-version: '14.3.1'
+ runs-on: 'macos-15-intel'
+ xcode-toolset-version: '16.4'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
@@ -340,8 +340,8 @@ jobs:
uses: ./.github/workflows/build-macos.yml
with:
platform: macos-aarch64
- runs-on: 'macos-14'
- xcode-toolset-version: '15.4'
+ runs-on: 'macos-15'
+ xcode-toolset-version: '16.4'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
@@ -432,9 +432,9 @@ jobs:
with:
platform: macos-aarch64
bootjdk-platform: macos-aarch64
- runs-on: macos-14
+ runs-on: macos-15
dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
- xcode-toolset-version: '15.4'
+ xcode-toolset-version: '16.4'
debug-suffix: -debug
test-windows-x64:
From 6b3162620bd808227ec7b4331ae6fc32ceb909e8 Mon Sep 17 00:00:00 2001
From: Naoto Sato
Date: Tue, 7 Oct 2025 17:21:13 +0000
Subject: [PATCH 053/160] 8368845: x-IBM930 uses incorrect character for Hex 42
60
Reviewed-by: sherman, rriggs, iris
---
make/data/charsetmapping/IBM930.c2b | 5 -----
make/data/charsetmapping/IBM930.map | 10 +---------
test/jdk/sun/nio/cs/mapping/CoderTest.java | 4 ++--
test/jdk/sun/nio/cs/mapping/ConverterTest.java | 5 +++--
test/jdk/sun/nio/cs/mapping/Cp930.b2c | 2 +-
test/jdk/sun/nio/cs/mapping/TestConv.java | 4 ++--
6 files changed, 9 insertions(+), 21 deletions(-)
diff --git a/make/data/charsetmapping/IBM930.c2b b/make/data/charsetmapping/IBM930.c2b
index 88754763fe3..72107424104 100644
--- a/make/data/charsetmapping/IBM930.c2b
+++ b/make/data/charsetmapping/IBM930.c2b
@@ -32,11 +32,6 @@
547d 92ca
53da 9b7e
446e f86f
-#
-# we should use this one instead of the 4260<-ff0d
-#4260 2212
-4260 ff0d
-#
426A 00A6
43A1 301C
444A 2014
diff --git a/make/data/charsetmapping/IBM930.map b/make/data/charsetmapping/IBM930.map
index 4b9dad9526b..7939e795bdf 100644
--- a/make/data/charsetmapping/IBM930.map
+++ b/make/data/charsetmapping/IBM930.map
@@ -25,13 +25,6 @@
# 4260 <--> 2212
# 426A <--> 00A6
#
-# Warning:
-# "our old" implementation seems agree with above "new" mappings
-# except the entries 4260 <-> 2212. To keep the "compatbility"
-# with the "old" implementation, I changed the entries "temporarily"
-# 4260 <-> 2212
-# 4260 <- ff0d
-#
00 0000
01 0001
02 0002
@@ -407,8 +400,7 @@ FF 009F
425D FF09
425E FF1B
425F FFE2
-#4260 FF0D
-4260 2212
+4260 FF0D
4261 FF0F
426A FFE4
426B FF0C
diff --git a/test/jdk/sun/nio/cs/mapping/CoderTest.java b/test/jdk/sun/nio/cs/mapping/CoderTest.java
index 05913a40535..2f766736743 100644
--- a/test/jdk/sun/nio/cs/mapping/CoderTest.java
+++ b/test/jdk/sun/nio/cs/mapping/CoderTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2025, Oracle and/or its 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
@@ -22,7 +22,7 @@
*/
/* @test
- @bug 4691554 6221056 6380723 6404504 6419565 6529796 8301119
+ @bug 4691554 6221056 6380723 6404504 6419565 6529796 8301119 8368845
@summary Test the supported New I/O coders
@modules jdk.charsets
@run main CoderTest
diff --git a/test/jdk/sun/nio/cs/mapping/ConverterTest.java b/test/jdk/sun/nio/cs/mapping/ConverterTest.java
index be8df03230c..8d33670b0a5 100644
--- a/test/jdk/sun/nio/cs/mapping/ConverterTest.java
+++ b/test/jdk/sun/nio/cs/mapping/ConverterTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2025, Oracle and/or its 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
@@ -26,7 +26,8 @@
* @summary test Bug 4199484
* @modules jdk.charsets
* @run main ConverterTest
- * @bug 4199484 4199599 4199601 4199602 4159519 4201529 4199604 4201532 4947038 6217210
+ * @bug 4199484 4199599 4199601 4199602 4159519 4201529 4199604 4201532 4947038
+ * 6217210 8368845
*/
import java.util.*;
diff --git a/test/jdk/sun/nio/cs/mapping/Cp930.b2c b/test/jdk/sun/nio/cs/mapping/Cp930.b2c
index 67cc93fd628..3cd45375e2d 100644
--- a/test/jdk/sun/nio/cs/mapping/Cp930.b2c
+++ b/test/jdk/sun/nio/cs/mapping/Cp930.b2c
@@ -340,7 +340,7 @@ F9 0039
0E425D0F FF09
0E425E0F FF1B
0E425F0F FFE2
-0E42600F 2212
+0E42600F FF0D
0E42610F FF0F
0E426A0F FFE4
0E426B0F FF0C
diff --git a/test/jdk/sun/nio/cs/mapping/TestConv.java b/test/jdk/sun/nio/cs/mapping/TestConv.java
index 2f4ea424f6c..3bd0b15a7d3 100644
--- a/test/jdk/sun/nio/cs/mapping/TestConv.java
+++ b/test/jdk/sun/nio/cs/mapping/TestConv.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2025, Oracle and/or its 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
@@ -22,7 +22,7 @@
*/
/* @test
- @bug 4179153 4652234 6529796
+ @bug 4179153 4652234 6529796 8368845
@summary Read code mapping table and check code conversion
@modules jdk.charsets
*/
From 7f070d356c479ae30fe84fcf4d322c0b693fa15a Mon Sep 17 00:00:00 2001
From: Mikael Vidstedt
Date: Tue, 7 Oct 2025 17:37:31 +0000
Subject: [PATCH 054/160] 8369246: Use https in make/devkit scripts
Reviewed-by: ayang, erikj
---
make/devkit/Tools.gmk | 10 +++++-----
make/devkit/createAutoconfBundle.sh | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk
index 6241674071c..f6ea6749f48 100644
--- a/make/devkit/Tools.gmk
+++ b/make/devkit/Tools.gmk
@@ -117,13 +117,13 @@ dependencies := gcc binutils ccache mpfr gmp mpc gdb
$(foreach dep,$(dependencies),$(eval $(dep)_ver := $(dep)-$($(dep)_ver_only)))
-GCC_URL := http://ftp.gnu.org/pub/gnu/gcc/$(gcc_ver)/$(gcc_ver).tar.xz
-BINUTILS_URL := http://ftp.gnu.org/pub/gnu/binutils/$(binutils_ver).tar.gz
+GCC_URL := https://ftp.gnu.org/pub/gnu/gcc/$(gcc_ver)/$(gcc_ver).tar.xz
+BINUTILS_URL := https://ftp.gnu.org/pub/gnu/binutils/$(binutils_ver).tar.gz
CCACHE_URL := https://github.com/ccache/ccache/releases/download/v$(ccache_ver_only)/$(ccache_ver).tar.xz
MPFR_URL := https://www.mpfr.org/$(mpfr_ver)/$(mpfr_ver).tar.bz2
-GMP_URL := http://ftp.gnu.org/pub/gnu/gmp/$(gmp_ver).tar.bz2
-MPC_URL := http://ftp.gnu.org/pub/gnu/mpc/$(mpc_ver).tar.gz
-GDB_URL := http://ftp.gnu.org/gnu/gdb/$(gdb_ver).tar.xz
+GMP_URL := https://ftp.gnu.org/pub/gnu/gmp/$(gmp_ver).tar.bz2
+MPC_URL := https://ftp.gnu.org/pub/gnu/mpc/$(mpc_ver).tar.gz
+GDB_URL := https://ftp.gnu.org/gnu/gdb/$(gdb_ver).tar.xz
REQUIRED_MIN_MAKE_MAJOR_VERSION := 4
ifneq ($(REQUIRED_MIN_MAKE_MAJOR_VERSION),)
diff --git a/make/devkit/createAutoconfBundle.sh b/make/devkit/createAutoconfBundle.sh
index ebe9c427f76..4697e4eb1e3 100644
--- a/make/devkit/createAutoconfBundle.sh
+++ b/make/devkit/createAutoconfBundle.sh
@@ -93,7 +93,7 @@ elif test "x$TARGET_PLATFORM" = xlinux_x64; then
rpm2cpio $OUTPUT_ROOT/m4-$M4_VERSION.el6.x86_64.rpm | cpio -d -i
elif test "x$TARGET_PLATFORM" = xlinux_x86; then
M4_VERSION=1.4.13-5
- wget http://yum.oracle.com/repo/OracleLinux/OL6/latest/i386/getPackage/m4-$M4_VERSION.el6.i686.rpm
+ wget https://yum.oracle.com/repo/OracleLinux/OL6/latest/i386/getPackage/m4-$M4_VERSION.el6.i686.rpm
cd $IMAGE_DIR
rpm2cpio $OUTPUT_ROOT/m4-$M4_VERSION.el6.i686.rpm | cpio -d -i
else
From 6bfd018beaf187940ebafc71885045b4aabca673 Mon Sep 17 00:00:00 2001
From: Phil Race
Date: Tue, 7 Oct 2025 19:08:22 +0000
Subject: [PATCH 055/160] 8366002: Beans.instantiate needs to describe the
lookup procedure
Reviewed-by: serb, aivanov
---
.../share/classes/java/beans/Beans.java | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/src/java.desktop/share/classes/java/beans/Beans.java b/src/java.desktop/share/classes/java/beans/Beans.java
index 313bfe98515..a95aeb45cbb 100644
--- a/src/java.desktop/share/classes/java/beans/Beans.java
+++ b/src/java.desktop/share/classes/java/beans/Beans.java
@@ -64,6 +64,22 @@ public class Beans {
*
* Instantiate a JavaBean.
*
+ * The bean is created based on a name relative to a class-loader.
+ * This name should be a {@linkplain ClassLoader##binary-name binary name} of a class such as "a.b.C".
+ *
+ * The given name can indicate either a serialized object or a class.
+ * We first try to treat the {@code beanName} as a serialized object
+ * name then as a class name.
+ *
+ * When using the {@code beanName} as a serialized object name we convert the
+ * given {@code beanName} to a resource pathname and add a trailing ".ser" suffix.
+ * We then try to load a serialized object from that resource.
+ *
+ * For example, given a {@code beanName} of "x.y", {@code Beans.instantiate} would first
+ * try to read a serialized object from the resource "x/y.ser" and if
+ * that failed it would try to load the class "x.y" and create an
+ * instance of that class.
+ *
* @return a JavaBean
* @param cls the class-loader from which we should create
* the bean. If this is null, then the system
@@ -84,6 +100,22 @@ public class Beans {
*
* Instantiate a JavaBean.
*
+ * The bean is created based on a name relative to a class-loader.
+ * This name should be a {@linkplain ClassLoader##binary-name binary name} of a class such as "a.b.C".
+ *
+ * The given name can indicate either a serialized object or a class.
+ * We first try to treat the {@code beanName} as a serialized object
+ * name then as a class name.
+ *
+ * When using the {@code beanName} as a serialized object name we convert the
+ * given {@code beanName} to a resource pathname and add a trailing ".ser" suffix.
+ * We then try to load a serialized object from that resource.
+ *
+ * For example, given a {@code beanName} of "x.y", {@code Beans.instantiate} would first
+ * try to read a serialized object from the resource "x/y.ser" and if
+ * that failed it would try to load the class "x.y" and create an
+ * instance of that class.
+ *
* @return a JavaBean
*
* @param cls the class-loader from which we should create
From 910bb68e5191f830ff6f3dff5753e4e5f6214a7b Mon Sep 17 00:00:00 2001
From: Archie Cobbs
Date: Tue, 7 Oct 2025 19:32:08 +0000
Subject: [PATCH 056/160] 8349847: Support configuring individual lint
categories as errors
Reviewed-by: vromero
---
.../com/sun/tools/javac/code/Lint.java | 29 +----
.../sun/tools/javac/main/JavaCompiler.java | 23 +++-
.../com/sun/tools/javac/main/Option.java | 18 +++
.../JavacProcessingEnvironment.java | 2 +-
.../tools/javac/resources/javac.properties | 10 +-
.../classes/com/sun/tools/javac/util/Log.java | 30 ++++-
.../com/sun/tools/javac/util/Options.java | 113 ++++++++++++++----
src/jdk.compiler/share/man/javac.md | 13 +-
.../tools/javac/warnings/WerrorLint.e1.out | 4 +
.../tools/javac/warnings/WerrorLint.e2.out | 5 +
.../tools/javac/warnings/WerrorLint.java | 23 ++++
.../tools/javac/warnings/WerrorLint.w1.out | 2 +
.../tools/javac/warnings/WerrorLint.w2.out | 3 +
13 files changed, 214 insertions(+), 61 deletions(-)
create mode 100644 test/langtools/tools/javac/warnings/WerrorLint.e1.out
create mode 100644 test/langtools/tools/javac/warnings/WerrorLint.e2.out
create mode 100644 test/langtools/tools/javac/warnings/WerrorLint.java
create mode 100644 test/langtools/tools/javac/warnings/WerrorLint.w1.out
create mode 100644 test/langtools/tools/javac/warnings/WerrorLint.w2.out
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
index 88c9da5d9e8..3a8b4e5dbea 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
@@ -147,33 +147,10 @@ public class Lint {
// Process command line options on demand to allow use of root Lint early during startup
private void initializeRootIfNeeded() {
-
- // Already initialized?
- if (values != null)
- return;
-
- // Initialize enabled categories based on "-Xlint" flags
- if (options.isSet(Option.XLINT) || options.isSet(Option.XLINT_CUSTOM, Option.LINT_CUSTOM_ALL)) {
- // If -Xlint or -Xlint:all is given, enable all categories by default
- values = EnumSet.allOf(LintCategory.class);
- } else if (options.isSet(Option.XLINT_CUSTOM, Option.LINT_CUSTOM_NONE)) {
- // if -Xlint:none is given, disable all categories by default
- values = LintCategory.newEmptySet();
- } else {
- // otherwise, enable on-by-default categories
- values = getDefaults();
+ if (values == null) {
+ values = options.getLintCategoriesOf(Option.XLINT, this::getDefaults);
+ suppressedValues = LintCategory.newEmptySet();
}
-
- // Look for specific overrides
- for (LintCategory lc : LintCategory.values()) {
- if (options.isLintExplicitlyEnabled(lc)) {
- values.add(lc);
- } else if (options.isLintExplicitlyDisabled(lc)) {
- values.remove(lc);
- }
- }
-
- suppressedValues = LintCategory.newEmptySet();
}
// Obtain the set of on-by-default categories. Note that for a few categories,
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java
index ee11304dce9..2469dc9e031 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java
@@ -31,6 +31,7 @@ import java.nio.file.InvalidPathException;
import java.nio.file.ReadOnlyFileSystemException;
import java.util.Collection;
import java.util.Comparator;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -55,6 +56,7 @@ import javax.tools.StandardLocation;
import com.sun.source.util.TaskEvent;
import com.sun.tools.javac.api.MultiTaskListener;
import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Lint.LintCategory;
import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
@@ -440,7 +442,8 @@ public class JavaCompiler {
context.get(DiagnosticListener.class) != null;
devVerbose = options.isSet("dev");
processPcks = options.isSet("process.packages");
- werror = options.isSet(WERROR);
+ werrorAny = options.isSet(WERROR) || options.isSet(WERROR_CUSTOM, Option.LINT_CUSTOM_ALL);
+ werrorLint = options.getLintCategoriesOf(WERROR, LintCategory::newEmptySet);
verboseCompilePolicy = options.isSet("verboseCompilePolicy");
@@ -513,9 +516,13 @@ public class JavaCompiler {
*/
protected boolean processPcks;
- /** Switch: treat warnings as errors
+ /** Switch: treat any kind of warning (lint or non-lint) as an error.
*/
- protected boolean werror;
+ protected boolean werrorAny;
+
+ /** Switch: treat lint warnings in the specified {@link LintCategory}s as errors.
+ */
+ protected EnumSet werrorLint;
/** Switch: is annotation processing requested explicitly via
* CompilationTask.setProcessors?
@@ -581,12 +588,20 @@ public class JavaCompiler {
*/
public int errorCount() {
log.reportOutstandingWarnings();
- if (werror && log.nerrors == 0 && log.nwarnings > 0) {
+ if (log.nerrors == 0 && log.nwarnings > 0 &&
+ (werrorAny || werrorLint.clone().removeAll(log.lintWarnings))) {
log.error(Errors.WarningsAndWerror);
}
return log.nerrors;
}
+ /**
+ * Should warnings in the given lint category be treated as errors due to a {@code -Werror} flag?
+ */
+ public boolean isWerror(LintCategory lc) {
+ return werrorAny || werrorLint.contains(lc);
+ }
+
protected final Queue stopIfError(CompileState cs, Queue queue) {
return shouldStop(cs) ? new ListBuffer() : queue;
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java
index 8d5ad4c4d78..c14767a7a8c 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java
@@ -563,6 +563,8 @@ public enum Option {
// treat warnings as errors
WERROR("-Werror", "opt.Werror", STANDARD, BASIC),
+ WERROR_CUSTOM("-Werror:", "opt.arg.Werror", "opt.Werror.custom", STANDARD, BASIC, ANYOF, getXLintChoices()),
+
// prompt after each error
// new Option("-prompt", "opt.prompt"),
PROMPT("-prompt", null, HIDDEN, BASIC),
@@ -1132,6 +1134,22 @@ public enum Option {
return Option.valueOf(name() + "_CUSTOM");
}
+ /**
+ * Like {@link #getCustom} but also requires that the custom option supports lint categories.
+ *
+ *
+ * In practice, that means {@code option} must be {@link Option#LINT} or {@link Option#WERROR}.
+ *
+ * @param option regular option
+ * @return corresponding lint custom option
+ * @throws IllegalArgumentException if no such option exists
+ */
+ public Option getLintCustom() {
+ if (this == XLINT || this == WERROR)
+ return getCustom();
+ throw new IllegalArgumentException();
+ }
+
public boolean isInBasicOptionGroup() {
return group == BASIC;
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
index b28f19bd3af..74d082d4b64 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
@@ -211,7 +211,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
}
fatalErrors = options.isSet("fatalEnterError");
showResolveErrors = options.isSet("showResolveErrors");
- werror = options.isSet(Option.WERROR);
+ werror = compiler.isWerror(PROCESSING);
fileManager = context.get(JavaFileManager.class);
platformAnnotations = initPlatformAnnotations();
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties
index 15a63da06eb..6d4276c794b 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties
@@ -95,7 +95,13 @@ javac.opt.source=\
Provide source compatibility with the specified Java SE release.\n\
Supported releases: \n {0}
javac.opt.Werror=\
- Terminate compilation if warnings occur
+ Terminate compilation if any warnings occur
+javac.opt.arg.Werror=\
+ (,)*
+javac.opt.Werror.custom=\
+ Specify lint categories for which warnings should terminate compilation,\n\
+ separated by comma. Precede a key by ''-'' to exclude the specified category.\n\
+ Use --help-lint to see the supported keys.
javac.opt.A=\
Options to pass to annotation processors
javac.opt.implicit=\
@@ -330,7 +336,7 @@ javac.opt.X=\
javac.opt.help=\
Print this help message
javac.opt.help.lint=\
- Print the supported keys for -Xlint
+ Print the supported keys for -Xlint and -Werror
javac.opt.help.lint.header=\
The supported keys for -Xlint are:
javac.opt.help.lint.enabled.by.default=\
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java
index 95458f339a1..24a77a751fd 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java
@@ -171,7 +171,7 @@ public class Log extends AbstractLog {
lint.isEnabled(category) : // then emit if the category is enabled
category.annotationSuppression ? // else emit if the category is not suppressed, where
!lint.isSuppressed(category) : // ...suppression happens via @SuppressWarnings
- !options.isLintDisabled(category); // ...suppression happens via -Xlint:-category
+ !options.isDisabled(Option.XLINT, category); // ...suppression happens via -Xlint:-category
if (!emit)
return;
}
@@ -553,10 +553,14 @@ public class Log extends AbstractLog {
*/
public int nerrors = 0;
- /** The number of warnings encountered so far.
+ /** The total number of warnings encountered so far.
*/
public int nwarnings = 0;
+ /** Tracks whether any warnings have been encountered in each {@link LintCategory}.
+ */
+ public final EnumSet lintWarnings = LintCategory.newEmptySet();
+
/** The number of errors encountered after MaxErrors was reached.
*/
public int nsuppressederrors = 0;
@@ -885,6 +889,7 @@ public class Log extends AbstractLog {
public void clear() {
recorded.clear();
sourceMap.clear();
+ lintWarnings.clear();
nerrors = 0;
nwarnings = 0;
nsuppressederrors = 0;
@@ -940,7 +945,6 @@ public class Log extends AbstractLog {
// Strict warnings are always emitted
if (diagnostic.isFlagSet(STRICT)) {
writeDiagnostic(diagnostic);
- nwarnings++;
return;
}
@@ -948,7 +952,6 @@ public class Log extends AbstractLog {
if (emitWarnings || diagnostic.isMandatory()) {
if (nwarnings < MaxWarnings) {
writeDiagnostic(diagnostic);
- nwarnings++;
} else {
nsuppressedwarns++;
}
@@ -959,7 +962,6 @@ public class Log extends AbstractLog {
if (diagnostic.isFlagSet(API) || shouldReport(diagnostic)) {
if (nerrors < MaxErrors) {
writeDiagnostic(diagnostic);
- nerrors++;
} else {
nsuppressederrors++;
}
@@ -973,9 +975,25 @@ public class Log extends AbstractLog {
}
/**
- * Write out a diagnostic.
+ * Write out a diagnostic and bump the warning and error counters as needed.
*/
protected void writeDiagnostic(JCDiagnostic diag) {
+
+ // Increment counter(s)
+ switch (diag.getType()) {
+ case WARNING:
+ nwarnings++;
+ Optional.of(diag)
+ .map(JCDiagnostic::getLintCategory)
+ .ifPresent(lintWarnings::add);
+ break;
+ case ERROR:
+ nerrors++;
+ break;
+ default:
+ break;
+ }
+
if (diagListener != null) {
diagListener.report(diag);
return;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java
index 32a31028b68..030e5b21758 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java
@@ -173,66 +173,139 @@ public class Options {
/**
* Determine if a specific {@link LintCategory} is enabled via a custom
- * option flag of the form {@code -Xlint}, {@code -Xlint:all}, or {@code -Xlint:key}.
+ * option flag of the form {@code -Flag}, {@code -Flag:all}, or {@code -Flag:key}.
+ *
+ *
+ * The given {@code option} must have a custom lint variant (available via {@link Option#getLintCustom}).
*
*
* Note: It's possible the category was also disabled; this method does not check that.
*
+ * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT})
* @param lc the {@link LintCategory} in question
- * @return true if {@code lc} has been enabled
+ * @return true if {@code lc} is enabled via {@code option}'s lint custom variant (e.g., {@link Option#XLINT_CUSTOM})
+ * @throws IllegalArgumentException if there is no lint custom variant of {@code option}
*/
- public boolean isLintEnabled(LintCategory lc) {
- return isLintExplicitlyEnabled(lc) ||
- isSet(Option.XLINT_CUSTOM) ||
- isSet(Option.XLINT_CUSTOM, Option.LINT_CUSTOM_ALL);
+ public boolean isEnabled(Option option, LintCategory lc) {
+ Option custom = option.getLintCustom();
+ return isExplicitlyEnabled(option, lc) || isSet(custom) || isSet(custom, Option.LINT_CUSTOM_ALL);
}
/**
* Determine if a specific {@link LintCategory} is disabled via a custom
- * option flag of the form {@code -Xlint:none} or {@code -Xlint:-key}.
+ * option flag of the form {@code -Flag:none} or {@code -Flag:-key}.
+ *
+ *
+ * The given {@code option} must have a custom lint variant (available via {@link Option#getLintCustom}).
*
*
* Note: It's possible the category was also enabled; this method does not check that.
*
+ * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT})
* @param lc the {@link LintCategory} in question
- * @return true if {@code lc} has been disabled
+ * @return true if {@code lc} is disabled via {@code option}'s lint custom variant (e.g., {@link Option#XLINT_CUSTOM})
+ * @throws IllegalArgumentException if there is no lint custom variant of {@code option}
*/
- public boolean isLintDisabled(LintCategory lc) {
- return isLintExplicitlyDisabled(lc) || isSet(Option.XLINT_CUSTOM, Option.LINT_CUSTOM_NONE);
+ public boolean isDisabled(Option option, LintCategory lc) {
+ return isExplicitlyDisabled(option, lc) || isSet(option.getLintCustom(), Option.LINT_CUSTOM_NONE);
}
/**
* Determine if a specific {@link LintCategory} is explicitly enabled via a custom
- * option flag of the form {@code -Xlint:key}.
+ * option flag of the form {@code -Flag:key}.
*
*
- * Note: This does not check for option flags of the form {@code -Xlint} or {@code -Xlint:all}.
+ * The given {@code option} must have a custom lint variant (available via {@link Option#getLintCustom}).
+ *
+ *
+ * Note: This does not check for option flags of the form {@code -Flag} or {@code -Flag:all}.
*
*
* Note: It's possible the category was also disabled; this method does not check that.
*
+ * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT})
* @param lc the {@link LintCategory} in question
- * @return true if {@code lc} has been explicitly enabled
+ * @return true if {@code lc} is explicitly enabled via {@code option}'s lint custom variant (e.g., {@link Option#XLINT_CUSTOM})
+ * @throws IllegalArgumentException if there is no lint custom variant of {@code option}
*/
- public boolean isLintExplicitlyEnabled(LintCategory lc) {
- return lc.optionList.stream().anyMatch(alias -> isSet(Option.XLINT_CUSTOM, alias));
+ public boolean isExplicitlyEnabled(Option option, LintCategory lc) {
+ Option customOption = option.getLintCustom();
+ return lc.optionList.stream().anyMatch(alias -> isSet(customOption, alias));
}
/**
* Determine if a specific {@link LintCategory} is explicitly disabled via a custom
- * option flag of the form {@code -Xlint:-key}.
+ * option flag of the form {@code -Flag:-key}.
*
*
- * Note: This does not check for an option flag of the form {@code -Xlint:none}.
+ * The given {@code option} must have a custom lint variant (available via {@link Option#getLintCustom}).
+ *
+ *
+ * Note: This does not check for an option flag of the form {@code -Flag:none}.
*
*
* Note: It's possible the category was also enabled; this method does not check that.
*
+ * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT})
* @param lc the {@link LintCategory} in question
- * @return true if {@code lc} has been explicitly disabled
+ * @return true if {@code lc} is explicitly disabled via {@code option}'s lint custom variant (e.g., {@link Option#XLINT_CUSTOM})
+ * @throws IllegalArgumentException if there is no lint custom variant of {@code option}
*/
- public boolean isLintExplicitlyDisabled(LintCategory lc) {
- return lc.optionList.stream().anyMatch(alias -> isSet(Option.XLINT_CUSTOM, "-" + alias));
+ public boolean isExplicitlyDisabled(Option option, LintCategory lc) {
+ Option customOption = option.getLintCustom();
+ return lc.optionList.stream().anyMatch(alias -> isSet(customOption, "-" + alias));
+ }
+
+ /**
+ * Collect the set of {@link LintCategory}s specified by option flag(s) of the form
+ * {@code -Flag} and/or {@code -Flag:[-]key,[-]key,...}.
+ *
+ *
+ * The given {@code option} must have a custom lint variant (available via {@link Option#getLintCustom}).
+ *
+ *
+ * The set of categories is calculated as follows. First, an initial set is created:
+ *
+ * - If {@code -Flag} or {@code -Flag:all} appears, the initial set contains all categories; otherwise,
+ *
- If {@code -Flag:none} appears, the initial set is empty; otherwise,
+ *
- The {@code defaults} parameter is invoked to construct an initial set.
+ *
+ * Next, for each lint category key {@code key}:
+ *
+ * - If {@code -Flag:key} flag appears, the corresponding category is added to the set; otherwise
+ *
- If {@code -Flag:-key} flag appears, the corresponding category is removed to the set
+ *
+ * Unrecognized {@code key}s are ignored.
+ *
+ * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT})
+ * @param defaults populates the default set, or null for an empty default set
+ * @return the specified set of categories
+ * @throws IllegalArgumentException if there is no lint custom variant of {@code option}
+ */
+ public EnumSet getLintCategoriesOf(Option option, Supplier extends EnumSet> defaults) {
+
+ // Create the initial set
+ EnumSet categories;
+ Option customOption = option.getLintCustom();
+ if (isSet(option) || isSet(customOption, Option.LINT_CUSTOM_ALL)) {
+ categories = EnumSet.allOf(LintCategory.class);
+ } else if (isSet(customOption, Option.LINT_CUSTOM_NONE)) {
+ categories = EnumSet.noneOf(LintCategory.class);
+ } else {
+ categories = defaults.get();
+ }
+
+ // Apply specific overrides
+ for (LintCategory category : LintCategory.values()) {
+ if (isExplicitlyEnabled(option, category)) {
+ categories.add(category);
+ } else if (isExplicitlyDisabled(option, category)) {
+ categories.remove(category);
+ }
+ }
+
+ // Done
+ return categories;
}
public void put(String name, String value) {
diff --git a/src/jdk.compiler/share/man/javac.md b/src/jdk.compiler/share/man/javac.md
index b8243cc78fb..46246624e53 100644
--- a/src/jdk.compiler/share/man/javac.md
+++ b/src/jdk.compiler/share/man/javac.md
@@ -1,5 +1,5 @@
---
-# Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 1994, 2025, Oracle and/or its 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
@@ -448,7 +448,16 @@ file system locations may be directories, JAR files or JMOD files.
: Prints version information.
`-Werror`
-: Terminates compilation when warnings occur.
+: Terminates compilation when any warnings occur; this includes warnings in all lint
+ categories, as well as non-lint warnings.
+
+`-Werror:`\[`-`\]*key*(`,`\[`-`\]*key*)\*
+: Specify lint categories for which warnings should terminate compilation. The keys
+ `all` and `none` include or exclude all categories (respectively); other keys include
+ the corresponding category, or exclude it if preceded by a hyphen (`-`). By default,
+ no categories are included. In order to terminate compilation, the category must also
+ be enabled (via [`-Xlint`](#option-Xlint-custom), if necessary).
+ See [`-Xlint`](#option-Xlint-custom) below for the list of lint category keys.
### Extra Options
diff --git a/test/langtools/tools/javac/warnings/WerrorLint.e1.out b/test/langtools/tools/javac/warnings/WerrorLint.e1.out
new file mode 100644
index 00000000000..ae99cfa5056
--- /dev/null
+++ b/test/langtools/tools/javac/warnings/WerrorLint.e1.out
@@ -0,0 +1,4 @@
+WerrorLint.java:20:19: compiler.warn.strictfp
+- compiler.err.warnings.and.werror
+1 error
+1 warning
diff --git a/test/langtools/tools/javac/warnings/WerrorLint.e2.out b/test/langtools/tools/javac/warnings/WerrorLint.e2.out
new file mode 100644
index 00000000000..1c9bd4d54f8
--- /dev/null
+++ b/test/langtools/tools/javac/warnings/WerrorLint.e2.out
@@ -0,0 +1,5 @@
+WerrorLint.java:20:19: compiler.warn.strictfp
+WerrorLint.java:21:30: compiler.warn.empty.if
+- compiler.err.warnings.and.werror
+1 error
+2 warnings
diff --git a/test/langtools/tools/javac/warnings/WerrorLint.java b/test/langtools/tools/javac/warnings/WerrorLint.java
new file mode 100644
index 00000000000..3331a664d55
--- /dev/null
+++ b/test/langtools/tools/javac/warnings/WerrorLint.java
@@ -0,0 +1,23 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8349847
+ *
+ * @compile -XDrawDiagnostics -Xlint:none WerrorLint.java
+ * @compile -XDrawDiagnostics -Xlint:none -Werror WerrorLint.java
+ * @compile -XDrawDiagnostics -Xlint:none -Werror:empty WerrorLint.java
+ * @compile -XDrawDiagnostics -Xlint:none -Werror:strictfp WerrorLint.java
+ * @compile/ref=WerrorLint.w2.out -XDrawDiagnostics -Xlint:all WerrorLint.java
+ * @compile/fail/ref=WerrorLint.e2.out -XDrawDiagnostics -Xlint:all -Werror WerrorLint.java
+ * @compile/fail/ref=WerrorLint.e2.out -XDrawDiagnostics -Xlint:all -Werror:empty WerrorLint.java
+ * @compile/fail/ref=WerrorLint.e2.out -XDrawDiagnostics -Xlint:all -Werror:strictfp WerrorLint.java
+ * @compile/ref=WerrorLint.w1.out -XDrawDiagnostics WerrorLint.java
+ * @compile/fail/ref=WerrorLint.e1.out -XDrawDiagnostics -Werror WerrorLint.java
+ * @compile/ref=WerrorLint.w1.out -XDrawDiagnostics -Werror:empty WerrorLint.java
+ * @compile/fail/ref=WerrorLint.e1.out -XDrawDiagnostics -Werror:strictfp WerrorLint.java
+ */
+
+class WerrorLint {
+ strictfp void m() { // [strictfp] - this category is enabled by default
+ if (hashCode() == 1) ; // [empty] - this category is disabled by default
+ }
+}
diff --git a/test/langtools/tools/javac/warnings/WerrorLint.w1.out b/test/langtools/tools/javac/warnings/WerrorLint.w1.out
new file mode 100644
index 00000000000..3e19de51033
--- /dev/null
+++ b/test/langtools/tools/javac/warnings/WerrorLint.w1.out
@@ -0,0 +1,2 @@
+WerrorLint.java:20:19: compiler.warn.strictfp
+1 warning
diff --git a/test/langtools/tools/javac/warnings/WerrorLint.w2.out b/test/langtools/tools/javac/warnings/WerrorLint.w2.out
new file mode 100644
index 00000000000..bac258706a6
--- /dev/null
+++ b/test/langtools/tools/javac/warnings/WerrorLint.w2.out
@@ -0,0 +1,3 @@
+WerrorLint.java:20:19: compiler.warn.strictfp
+WerrorLint.java:21:30: compiler.warn.empty.if
+2 warnings
From 4ee6079b11034e7de8be72cd2832fb717c2f140d Mon Sep 17 00:00:00 2001
From: Mikael Vidstedt
Date: Wed, 8 Oct 2025 02:05:20 +0000
Subject: [PATCH 057/160] 8369328: Use uppercase variable names in the devkit
makefiles
Reviewed-by: erikj
---
make/devkit/Makefile | 50 +++++++--------
make/devkit/Tools.gmk | 146 +++++++++++++++++++++---------------------
2 files changed, 98 insertions(+), 98 deletions(-)
diff --git a/make/devkit/Makefile b/make/devkit/Makefile
index ffa23508a13..30e0dce0839 100644
--- a/make/devkit/Makefile
+++ b/make/devkit/Makefile
@@ -57,61 +57,61 @@
COMMA := ,
-os := $(shell uname -o)
-cpu := $(shell uname -m)
+OS := $(shell uname -o)
+CPU := $(shell uname -m)
# Figure out what platform this is building on.
-me := $(cpu)-$(if $(findstring Linux,$(os)),linux-gnu)
+ME := $(CPU)-$(if $(findstring Linux,$(OS)),linux-gnu)
-$(info Building on platform $(me))
+$(info Building on platform $(ME))
#
# By default just build for the current platform, which is assumed to be Linux
#
ifeq ($(TARGETS), )
- platforms := $(me)
- host_platforms := $(platforms)
+ PLATFORMS := $(ME)
+ HOST_PLATFORMS := $(PLATFORMS)
else
- platforms := $(subst $(COMMA), , $(TARGETS))
- host_platforms := $(me)
+ PLATFORMS := $(subst $(COMMA), , $(TARGETS))
+ HOST_PLATFORMS := $(ME)
endif
-target_platforms := $(platforms)
-$(info host_platforms $(host_platforms))
-$(info target_platforms $(target_platforms))
+TARGET_PLATFORMS := $(PLATFORMS)
+$(info HOST_PLATFORMS $(HOST_PLATFORMS))
+$(info TARGET_PLATFORMS $(TARGET_PLATFORMS))
-all compile : $(platforms)
+all compile : $(PLATFORMS)
ifeq ($(SKIP_ME), )
- $(foreach p,$(filter-out $(me),$(platforms)),$(eval $(p) : $$(me)))
+ $(foreach p,$(filter-out $(ME),$(PLATFORMS)),$(eval $(p) : $$(ME)))
endif
OUTPUT_ROOT = $(abspath ../../build/devkit)
RESULT = $(OUTPUT_ROOT)/result
-submakevars = HOST=$@ BUILD=$(me) RESULT=$(RESULT) OUTPUT_ROOT=$(OUTPUT_ROOT)
+SUBMAKEVARS = HOST=$@ BUILD=$(ME) RESULT=$(RESULT) OUTPUT_ROOT=$(OUTPUT_ROOT)
-$(host_platforms) :
+$(HOST_PLATFORMS) :
@echo 'Building compilers for $@'
- @echo 'Targets: $(target_platforms)'
- for p in $(filter $@, $(target_platforms)) $(filter-out $@, $(target_platforms)); do \
- $(MAKE) -f Tools.gmk download-rpms $(submakevars) \
+ @echo 'Targets: $(TARGET_PLATFORMS)'
+ for p in $(filter $@, $(TARGET_PLATFORMS)) $(filter-out $@, $(TARGET_PLATFORMS)); do \
+ $(MAKE) -f Tools.gmk download-rpms $(SUBMAKEVARS) \
TARGET=$$p PREFIX=$(RESULT)/$@-to-$$p && \
- $(MAKE) -f Tools.gmk all $(submakevars) \
+ $(MAKE) -f Tools.gmk all $(SUBMAKEVARS) \
TARGET=$$p PREFIX=$(RESULT)/$@-to-$$p && \
- $(MAKE) -f Tools.gmk ccache $(submakevars) \
+ $(MAKE) -f Tools.gmk ccache $(SUBMAKEVARS) \
TARGET=$@ PREFIX=$(RESULT)/$@-to-$$p || exit 1 ; \
done
@echo 'All done"'
-today := $(shell date +%Y%m%d)
+TODAY := $(shell date +%Y%m%d)
define Mktar
- $(1)-to-$(2)_tar = $$(RESULT)/sdk-$(1)-to-$(2)-$$(today).tar.gz
+ $(1)-to-$(2)_tar = $$(RESULT)/sdk-$(1)-to-$(2)-$$(TODAY).tar.gz
$$($(1)-to-$(2)_tar) : PLATFORM = $(1)-to-$(2)
TARFILES += $$($(1)-to-$(2)_tar)
endef
-$(foreach p,$(host_platforms),$(foreach t,$(target_platforms),$(eval $(call Mktar,$(p),$(t)))))
+$(foreach p,$(HOST_PLATFORMS),$(foreach t,$(TARGET_PLATFORMS),$(eval $(call Mktar,$(p),$(t)))))
tars : all $(TARFILES)
onlytars : $(TARFILES)
@@ -119,9 +119,9 @@ onlytars : $(TARFILES)
$(MAKE) -r -f Tars.gmk SRC_DIR=$(RESULT)/$(PLATFORM) TAR_FILE=$@
clean :
- rm -rf $(addprefix ../../build/devkit/, result $(host_platforms))
+ rm -rf $(addprefix ../../build/devkit/, result $(HOST_PLATFORMS))
dist-clean: clean
rm -rf $(addprefix ../../build/devkit/, src download)
FORCE :
-.PHONY : all compile tars $(configs) $(host_platforms) clean dist-clean
+.PHONY : all compile tars $(HOST_PLATFORMS) clean dist-clean
diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk
index f6ea6749f48..77a201d0c38 100644
--- a/make/devkit/Tools.gmk
+++ b/make/devkit/Tools.gmk
@@ -39,7 +39,7 @@
# Fix this...
#
-uppercase = $(shell echo $1 | tr a-z A-Z)
+lowercase = $(shell echo $1 | tr A-Z a-z)
$(info TARGET=$(TARGET))
$(info HOST=$(HOST))
@@ -104,26 +104,26 @@ endif
################################################################################
# Define external dependencies
-gcc_ver_only := 14.2.0
-binutils_ver_only := 2.43
-ccache_ver_only := 4.10.2
+GCC_VER_ONLY := 14.2.0
+BINUTILS_VER_ONLY := 2.43
+CCACHE_VER_ONLY := 4.10.2
CCACHE_CMAKE_BASED := 1
-mpfr_ver_only := 4.2.1
-gmp_ver_only := 6.3.0
-mpc_ver_only := 1.3.1
-gdb_ver_only := 15.2
+MPFR_VER_ONLY := 4.2.1
+GMP_VER_ONLY := 6.3.0
+MPC_VER_ONLY := 1.3.1
+GDB_VER_ONLY := 15.2
-dependencies := gcc binutils ccache mpfr gmp mpc gdb
+DEPENDENCIES := GCC BINUTILS CCACHE MPFR GMP MPC GDB
-$(foreach dep,$(dependencies),$(eval $(dep)_ver := $(dep)-$($(dep)_ver_only)))
+$(foreach dep,$(DEPENDENCIES),$(eval $(dep)_VER := $(call lowercase,$(dep)-$($(dep)_VER_ONLY))))
-GCC_URL := https://ftp.gnu.org/pub/gnu/gcc/$(gcc_ver)/$(gcc_ver).tar.xz
-BINUTILS_URL := https://ftp.gnu.org/pub/gnu/binutils/$(binutils_ver).tar.gz
-CCACHE_URL := https://github.com/ccache/ccache/releases/download/v$(ccache_ver_only)/$(ccache_ver).tar.xz
-MPFR_URL := https://www.mpfr.org/$(mpfr_ver)/$(mpfr_ver).tar.bz2
-GMP_URL := https://ftp.gnu.org/pub/gnu/gmp/$(gmp_ver).tar.bz2
-MPC_URL := https://ftp.gnu.org/pub/gnu/mpc/$(mpc_ver).tar.gz
-GDB_URL := https://ftp.gnu.org/gnu/gdb/$(gdb_ver).tar.xz
+GCC_URL := https://ftp.gnu.org/pub/gnu/gcc/$(GCC_VER)/$(GCC_VER).tar.xz
+BINUTILS_URL := https://ftp.gnu.org/pub/gnu/binutils/$(BINUTILS_VER).tar.gz
+CCACHE_URL := https://github.com/ccache/ccache/releases/download/v$(CCACHE_VER_ONLY)/$(CCACHE_VER).tar.xz
+MPFR_URL := https://www.mpfr.org/$(MPFR_VER)/$(MPFR_VER).tar.bz2
+GMP_URL := https://ftp.gnu.org/pub/gnu/gmp/$(GMP_VER).tar.bz2
+MPC_URL := https://ftp.gnu.org/pub/gnu/mpc/$(MPC_VER).tar.gz
+GDB_URL := https://ftp.gnu.org/gnu/gdb/$(GDB_VER).tar.xz
REQUIRED_MIN_MAKE_MAJOR_VERSION := 4
ifneq ($(REQUIRED_MIN_MAKE_MAJOR_VERSION),)
@@ -180,10 +180,10 @@ DOWNLOAD_RPMS := $(DOWNLOAD)/rpms/$(TARGET)-$(LINUX_VERSION)
SRCDIR := $(OUTPUT_ROOT)/src
# Marker file for unpacking rpms
-rpms := $(SYSROOT)/rpms_unpacked
+RPMS := $(SYSROOT)/rpms_unpacked
# Need to patch libs that are linker scripts to use non-absolute paths
-libs := $(SYSROOT)/libs_patched
+LIBS := $(SYSROOT)/libs_patched
################################################################################
# Download RPMs
@@ -228,7 +228,7 @@ define Download
endef
# Download and unpack all source packages
-$(foreach dep,$(dependencies),$(eval $(call Download,$(call uppercase,$(dep)))))
+$(foreach dep,$(DEPENDENCIES),$(eval $(call Download,$(dep))))
################################################################################
# Unpack RPMS
@@ -250,7 +250,7 @@ RPM_FILE_LIST := $(sort $(foreach a, $(RPM_ARCHS), \
# Note. For building linux you should install rpm2cpio.
define unrpm
$(SYSROOT)/$(notdir $(1)).unpacked : $(1)
- $$(rpms) : $(SYSROOT)/$(notdir $(1)).unpacked
+ $$(RPMS) : $(SYSROOT)/$(notdir $(1)).unpacked
endef
%.unpacked :
@@ -277,7 +277,7 @@ $(foreach p,$(RPM_FILE_LIST),$(eval $(call unrpm,$(p))))
# have it anyway, but just to make sure...
# Patch libc.so and libpthread.so to force linking against libraries in sysroot
# and not the ones installed on the build machine.
-$(libs) : $(rpms)
+$(LIBS) : $(RPMS)
@echo Patching libc and pthreads
@(for f in `find $(SYSROOT) -name libc.so -o -name libpthread.so`; do \
(cat $$f | sed -e 's|/usr/lib64/||g' \
@@ -293,10 +293,10 @@ $(libs) : $(rpms)
# Create links for ffi header files so that they become visible by default when using the
# devkit.
ifeq ($(ARCH), x86_64)
- $(SYSROOT)/usr/include/ffi.h: $(rpms)
+ $(SYSROOT)/usr/include/ffi.h: $(RPMS)
cd $(@D) && rm -f $(@F) && ln -s ../lib/libffi-*/include/$(@F) .
- $(SYSROOT)/usr/include/ffitarget.h: $(rpms)
+ $(SYSROOT)/usr/include/ffitarget.h: $(RPMS)
cd $(@D) && rm -f $(@F) && ln -s ../lib/libffi-*/include/$(@F) .
SYSROOT_LINKS += $(SYSROOT)/usr/include/ffi.h $(SYSROOT)/usr/include/ffitarget.h
@@ -305,7 +305,7 @@ endif
################################################################################
# Define marker files for each source package to be compiled
-$(foreach dep,$(dependencies),$(eval $(dep) = $(TARGETDIR)/$($(dep)_ver).done))
+$(foreach dep,$(DEPENDENCIES),$(eval $(dep) = $(TARGETDIR)/$($(dep)_VER).done))
################################################################################
@@ -345,48 +345,48 @@ TOOLS ?= $(call declare_tools,_FOR_TARGET,$(TARGET)-)
# CFLAG_ to most likely -m32.
define mk_bfd
$$(info Libs for $(1))
- $$(BUILDDIR)/$$(binutils_ver)-$(subst /,-,$(1))/Makefile \
+ $$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile \
: CFLAGS += $$(CFLAGS_$(1))
- $$(BUILDDIR)/$$(binutils_ver)-$(subst /,-,$(1))/Makefile \
+ $$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile \
: LIBDIRS = --libdir=$(TARGETDIR)/$(1)
- bfdlib += $$(TARGETDIR)/$$(binutils_ver)-$(subst /,-,$(1)).done
- bfdmakes += $$(BUILDDIR)/$$(binutils_ver)-$(subst /,-,$(1))/Makefile
+ BFDLIB += $$(TARGETDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1)).done
+ BFDMAKES += $$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile
endef
# Create one set of bfds etc for each multilib arch
$(foreach l,$(LIBDIRS),$(eval $(call mk_bfd,$(l))))
# Only build these two libs.
-$(bfdlib) : MAKECMD = all-libiberty all-bfd
-$(bfdlib) : INSTALLCMD = install-libiberty install-bfd
+$(BFDLIB) : MAKECMD = all-libiberty all-bfd
+$(BFDLIB) : INSTALLCMD = install-libiberty install-bfd
# Building targets libbfd + libiberty. HOST==TARGET, i.e not
# for a cross env.
-$(bfdmakes) : CONFIG = --target=$(TARGET) \
+$(BFDMAKES) : CONFIG = --target=$(TARGET) \
--host=$(TARGET) --build=$(BUILD) \
--prefix=$(TARGETDIR) \
--with-sysroot=$(SYSROOT) \
$(LIBDIRS)
-$(bfdmakes) : TOOLS = $(call declare_tools,_FOR_TARGET,$(TARGET)-) $(call declare_tools,,$(TARGET)-)
+$(BFDMAKES) : TOOLS = $(call declare_tools,_FOR_TARGET,$(TARGET)-) $(call declare_tools,,$(TARGET)-)
################################################################################
-$(gcc) \
- $(binutils) \
- $(gmp) \
- $(mpfr) \
- $(mpc) \
- $(bfdmakes) \
- $(ccache) : ENVS += $(TOOLS)
+$(GCC) \
+ $(BINUTILS) \
+ $(GMP) \
+ $(MPFR) \
+ $(MPC) \
+ $(BFDMAKES) \
+ $(CCACHE) : ENVS += $(TOOLS)
# libdir to work around hateful bfd stuff installing into wrong dirs...
# ensure we have 64 bit bfd support in the HOST library. I.e our
# compiler on i686 will know 64 bit symbols, BUT later
# we build just the libs again for TARGET, then with whatever the arch
# wants.
-$(BUILDDIR)/$(binutils_ver)/Makefile : CONFIG += --enable-64-bit-bfd --libdir=$(PREFIX)/$(word 1,$(LIBDIRS))
+$(BUILDDIR)/$(BINUTILS_VER)/Makefile : CONFIG += --enable-64-bit-bfd --libdir=$(PREFIX)/$(word 1,$(LIBDIRS))
ifeq ($(filter $(ARCH), s390x riscv64 ppc64le), )
# gold compiles but cannot link properly on s390x @ gcc 13.2 and Fedore 41
@@ -397,8 +397,8 @@ endif
# Makefile creation. Simply run configure in build dir.
# Setting CFLAGS to -O2 generates a much faster ld.
-$(bfdmakes) \
-$(BUILDDIR)/$(binutils_ver)/Makefile \
+$(BFDMAKES) \
+$(BUILDDIR)/$(BINUTILS_VER)/Makefile \
: $(BINUTILS_CFG)
$(info Configuring $@. Log in $(@D)/log.config)
@mkdir -p $(@D)
@@ -417,7 +417,7 @@ $(BUILDDIR)/$(binutils_ver)/Makefile \
) > $(@D)/log.config 2>&1
@echo 'done'
-$(BUILDDIR)/$(mpfr_ver)/Makefile \
+$(BUILDDIR)/$(MPFR_VER)/Makefile \
: $(MPFR_CFG)
$(info Configuring $@. Log in $(@D)/log.config)
@mkdir -p $(@D)
@@ -432,7 +432,7 @@ $(BUILDDIR)/$(mpfr_ver)/Makefile \
) > $(@D)/log.config 2>&1
@echo 'done'
-$(BUILDDIR)/$(gmp_ver)/Makefile \
+$(BUILDDIR)/$(GMP_VER)/Makefile \
: $(GMP_CFG)
$(info Configuring $@. Log in $(@D)/log.config)
@mkdir -p $(@D)
@@ -449,7 +449,7 @@ $(BUILDDIR)/$(gmp_ver)/Makefile \
) > $(@D)/log.config 2>&1
@echo 'done'
-$(BUILDDIR)/$(mpc_ver)/Makefile \
+$(BUILDDIR)/$(MPC_VER)/Makefile \
: $(MPC_CFG)
$(info Configuring $@. Log in $(@D)/log.config)
@mkdir -p $(@D)
@@ -468,11 +468,11 @@ $(BUILDDIR)/$(mpc_ver)/Makefile \
# Only valid if glibc target -> linux
# proper destructor handling for c++
ifneq (,$(findstring linux,$(TARGET)))
- $(BUILDDIR)/$(gcc_ver)/Makefile : CONFIG += --enable-__cxa_atexit
+ $(BUILDDIR)/$(GCC_VER)/Makefile : CONFIG += --enable-__cxa_atexit
endif
ifeq ($(ARCH), armhfp)
- $(BUILDDIR)/$(gcc_ver)/Makefile : CONFIG += --with-float=hard
+ $(BUILDDIR)/$(GCC_VER)/Makefile : CONFIG += --with-float=hard
endif
ifneq ($(filter riscv64 ppc64le s390x, $(ARCH)), )
@@ -487,7 +487,7 @@ endif
# skip native language.
# and link and assemble with the binutils we created
# earlier, so --with-gnu*
-$(BUILDDIR)/$(gcc_ver)/Makefile \
+$(BUILDDIR)/$(GCC_VER)/Makefile \
: $(GCC_CFG)
$(info Configuring $@. Log in $(@D)/log.config)
mkdir -p $(@D)
@@ -509,17 +509,17 @@ $(BUILDDIR)/$(gcc_ver)/Makefile \
@echo 'done'
# need binutils for gcc
-$(gcc) : $(binutils)
+$(GCC) : $(BINUTILS)
# as of 4.3 or so need these for doing config
-$(BUILDDIR)/$(gcc_ver)/Makefile : $(gmp) $(mpfr) $(mpc)
-$(mpfr) : $(gmp)
-$(mpc) : $(gmp) $(mpfr)
+$(BUILDDIR)/$(GCC_VER)/Makefile : $(GMP) $(MPFR) $(MPC)
+$(MPFR) : $(GMP)
+$(MPC) : $(GMP) $(MPFR)
################################################################################
# Build gdb but only where host and target match
ifeq ($(HOST), $(TARGET))
- $(BUILDDIR)/$(gdb_ver)/Makefile: $(GDB_CFG)
+ $(BUILDDIR)/$(GDB_VER)/Makefile: $(GDB_CFG)
$(info Configuring $@. Log in $(@D)/log.config)
mkdir -p $(@D)
( \
@@ -532,9 +532,9 @@ ifeq ($(HOST), $(TARGET))
) > $(@D)/log.config 2>&1
@echo 'done'
- $(gdb): $(gcc)
+ $(GDB): $(GCC)
else
- $(BUILDDIR)/$(gdb_ver)/Makefile:
+ $(BUILDDIR)/$(GDB_VER)/Makefile:
$(info Faking $@, not used when cross-compiling)
mkdir -p $(@D)
echo "install:" > $@
@@ -543,7 +543,7 @@ endif
################################################################################
# very straightforward. just build a ccache. it is only for host.
-$(BUILDDIR)/$(ccache_ver)/Makefile \
+$(BUILDDIR)/$(CCACHE_VER)/Makefile \
: $(CCACHE_SRC_MARKER)
$(info Configuring $@. Log in $(@D)/log.config)
@mkdir -p $(@D)
@@ -554,12 +554,12 @@ $(BUILDDIR)/$(ccache_ver)/Makefile \
) > $(@D)/log.config 2>&1
@echo 'done'
-gccpatch = $(TARGETDIR)/gcc-patched
+GCC_PATCHED = $(TARGETDIR)/gcc-patched
################################################################################
# For some reason cpp is not created as a target-compiler
ifeq ($(HOST),$(TARGET))
- $(gccpatch) : $(gcc) link_libs
+ $(GCC_PATCHED) : $(GCC) link_libs
@echo -n 'Creating compiler symlinks...'
@for f in cpp; do \
if [ ! -e $(PREFIX)/bin/$(TARGET)-$$f ]; \
@@ -587,7 +587,7 @@ ifeq ($(HOST),$(TARGET))
done;)
@echo 'done'
else
- $(gccpatch) :
+ $(GCC_PATCHED) :
@echo 'done'
endif
@@ -615,7 +615,7 @@ $(PREFIX)/devkit.info:
echo '# This file describes to configure how to interpret the contents of this' >> $@
echo '# devkit' >> $@
echo '' >> $@
- echo 'DEVKIT_NAME="$(gcc_ver) - $(LINUX_VERSION)"' >> $@
+ echo 'DEVKIT_NAME="$(GCC_VER) - $(LINUX_VERSION)"' >> $@
echo 'DEVKIT_TOOLCHAIN_PATH="$$DEVKIT_ROOT/bin"' >> $@
echo 'DEVKIT_SYSROOT="$$DEVKIT_ROOT/$(TARGET)/sysroot"' >> $@
echo 'DEVKIT_EXTRA_PATH="$$DEVKIT_ROOT/bin"' >> $@
@@ -651,32 +651,32 @@ ifeq ($(TARGET), $(HOST))
@echo 'Creating missing $* soft link'
ln -s $(TARGET)-$* $@
- missing-links := $(addprefix $(PREFIX)/bin/, \
- addr2line ar as c++ c++filt dwp elfedit g++ gcc gcc-$(gcc_ver_only) gprof ld ld.bfd \
+ MISSING_LINKS := $(addprefix $(PREFIX)/bin/, \
+ addr2line ar as c++ c++filt dwp elfedit g++ gcc gcc-$(GCC_VER_ONLY) gprof ld ld.bfd \
ld.gold nm objcopy objdump ranlib readelf size strings strip)
endif
# Add link to work around "plugin needed to handle lto object" (JDK-8344272)
-$(PREFIX)/lib/bfd-plugins/liblto_plugin.so: $(PREFIX)/libexec/gcc/$(TARGET)/$(gcc_ver_only)/liblto_plugin.so
+$(PREFIX)/lib/bfd-plugins/liblto_plugin.so: $(PREFIX)/libexec/gcc/$(TARGET)/$(GCC_VER_ONLY)/liblto_plugin.so
@echo 'Creating missing $(@F) soft link'
@mkdir -p $(@D)
ln -s $$(realpath -s --relative-to=$(@D) $<) $@
-missing-links += $(PREFIX)/lib/bfd-plugins/liblto_plugin.so
+MISSING_LINKS += $(PREFIX)/lib/bfd-plugins/liblto_plugin.so
################################################################################
-bfdlib : $(bfdlib)
-binutils : $(binutils)
-rpms : $(rpms)
-libs : $(libs)
+bfdlib : $(BFDLIB)
+binutils : $(BINUTILS)
+rpms : $(RPMS)
+libs : $(LIBS)
sysroot : rpms libs
-gcc : sysroot $(gcc) $(gccpatch)
-gdb : $(gdb)
-all : binutils gcc bfdlib $(PREFIX)/devkit.info $(missing-links) $(SYSROOT_LINKS) \
+gcc : sysroot $(GCC) $(GCC_PATCHED)
+gdb : $(GDB)
+all : binutils gcc bfdlib $(PREFIX)/devkit.info $(MISSING_LINKS) $(SYSROOT_LINKS) \
$(THESE_MAKEFILES) gdb
# this is only built for host. so separate.
-ccache : $(ccache)
+ccache : $(CCACHE)
.PHONY : gcc all binutils bfdlib link_libs rpms libs sysroot
From 650fd35b3b30bf16e8caad968bd335d423c87b7d Mon Sep 17 00:00:00 2001
From: Prasanta Sadhukhan
Date: Wed, 8 Oct 2025 03:00:30 +0000
Subject: [PATCH 058/160] 8335646: Nimbus : JLabel not painted with LAF defined
foreground color on Ubuntu 24.04
Reviewed-by: aivanov, dnguyen, serb
---
test/jdk/javax/swing/plaf/basic/BasicHTML/bug4248210.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/jdk/javax/swing/plaf/basic/BasicHTML/bug4248210.java b/test/jdk/javax/swing/plaf/basic/BasicHTML/bug4248210.java
index fddfbb28384..54f7744ee90 100644
--- a/test/jdk/javax/swing/plaf/basic/BasicHTML/bug4248210.java
+++ b/test/jdk/javax/swing/plaf/basic/BasicHTML/bug4248210.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2025, Oracle and/or its 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
@@ -71,7 +71,7 @@ public class bug4248210 {
UIManager.getDefaults().put("Label.foreground", labelColor);
}
- JLabel label = new JLabel("Can You Read This?");
+ JLabel label = new JLabel("\u2588 \u2588 \u2588 \u2588");
label.setSize(150, 30);
BufferedImage img = paintToImage(label);
From 2ac24bf1bac9c32704ebd72b93a75819b9404063 Mon Sep 17 00:00:00 2001
From: Emanuel Peter
Date: Wed, 8 Oct 2025 03:06:29 +0000
Subject: [PATCH 059/160] 8367389: C2 SuperWord: refactor VTransform to model
the whole loop instead of just the basic block
Reviewed-by: roland, mhaessig
---
src/hotspot/share/opto/phasetype.hpp | 7 +-
src/hotspot/share/opto/superword.cpp | 220 +++++-------------
src/hotspot/share/opto/superword.hpp | 6 +-
.../share/opto/superwordVTransformBuilder.cpp | 60 +++--
.../share/opto/superwordVTransformBuilder.hpp | 2 +
src/hotspot/share/opto/vectorization.cpp | 74 +++++-
src/hotspot/share/opto/vectorization.hpp | 102 +++++---
src/hotspot/share/opto/vtransform.cpp | 149 ++++++++----
src/hotspot/share/opto/vtransform.hpp | 147 +++++++-----
.../lib/ir_framework/CompilePhase.java | 6 +-
10 files changed, 447 insertions(+), 326 deletions(-)
diff --git a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp
index 5c733c7dc0a..f24938b51c1 100644
--- a/src/hotspot/share/opto/phasetype.hpp
+++ b/src/hotspot/share/opto/phasetype.hpp
@@ -89,10 +89,9 @@
flags(PHASEIDEALLOOP2, "PhaseIdealLoop 2") \
flags(PHASEIDEALLOOP3, "PhaseIdealLoop 3") \
flags(AUTO_VECTORIZATION1_BEFORE_APPLY, "AutoVectorization 1, before Apply") \
- flags(AUTO_VECTORIZATION2_AFTER_REORDER, "AutoVectorization 2, after Apply Memop Reordering") \
- flags(AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT, "AutoVectorization 3, after Adjusting Pre-loop Limit") \
- flags(AUTO_VECTORIZATION4_AFTER_SPECULATIVE_RUNTIME_CHECKS, "AutoVectorization 4, after Adding Speculative Runtime Checks") \
- flags(AUTO_VECTORIZATION5_AFTER_APPLY, "AutoVectorization 5, after Apply") \
+ flags(AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT, "AutoVectorization 2, after Adjusting Pre-loop Limit") \
+ flags(AUTO_VECTORIZATION4_AFTER_SPECULATIVE_RUNTIME_CHECKS, "AutoVectorization 3, after Adding Speculative Runtime Checks") \
+ flags(AUTO_VECTORIZATION5_AFTER_APPLY, "AutoVectorization 4, after Apply") \
flags(BEFORE_CCP1, "Before PhaseCCP 1") \
flags(CCP1, "PhaseCCP 1") \
flags(ITER_GVN2, "Iter GVN 2") \
diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp
index 2b3928781b8..41a4339e4c9 100644
--- a/src/hotspot/share/opto/superword.cpp
+++ b/src/hotspot/share/opto/superword.cpp
@@ -40,7 +40,7 @@ SuperWord::SuperWord(const VLoopAnalyzer &vloop_analyzer) :
NOT_PRODUCT(COMMA is_trace_superword_packset())
NOT_PRODUCT(COMMA is_trace_superword_rejections())
),
- _mem_ref_for_main_loop_alignment(nullptr),
+ _vpointer_for_main_loop_alignment(nullptr),
_aw_for_main_loop_alignment(0),
_do_vector_loop(phase()->C->do_vector_loop()), // whether to do vectorization/simd style
_num_work_vecs(0), // amount of vector work we have
@@ -455,11 +455,16 @@ bool SuperWord::transform_loop() {
//
// 8) The pairs are combined into vector sized packs.
//
-// 9) Reorder the memory slices to co-locate members of the memory packs.
+// 9) The packs are split and filtered, to ensure correctness and that
+// all packs have corresponding vector nodes implemented in the backend.
//
-// 10) Generate ideal vector nodes for the final set of packs and where necessary,
-// inserting scalar promotion, vector creation from multiple scalars, and
-// extraction of scalar values from vectors.
+// 10) VTransform (see vtransform.hpp)
+// - construct from PackSet
+// - schedule (detect circles)
+// - apply
+// - align main loop
+// - add runtime checks (aliasing and alignment)
+// - build new loop with vector C2 nodes
//
// Runtime Checks:
// Some required properties cannot be proven statically, and require a
@@ -498,7 +503,7 @@ bool SuperWord::SLP_extract() {
DEBUG_ONLY(verify_packs();)
DEBUG_ONLY(verify_no_extract());
- return schedule_and_apply();
+ return do_vtransform();
}
int SuperWord::MemOp::cmp_by_group(MemOp* a, MemOp* b) {
@@ -660,39 +665,9 @@ void SuperWord::create_adjacent_memop_pairs_in_one_group(const GrowableArrayfast_outs(imax); i < imax; i++) {
- PhiNode* phi = cl->fast_out(i)->isa_Phi();
- if (phi != nullptr && _vloop.in_bb(phi) && phi->is_memory_phi()) {
- Node* phi_tail = phi->in(LoopNode::LoopBackControl);
- if (phi_tail != phi->in(LoopNode::EntryControl)) {
- _heads.push(phi);
- _tails.push(phi_tail->as_Mem());
- }
- }
- }
-
- NOT_PRODUCT( if (_vloop.is_trace_memory_slices()) { print(); } )
-}
-
-#ifndef PRODUCT
-void VLoopMemorySlices::print() const {
- tty->print_cr("\nVLoopMemorySlices::print: %s",
- heads().length() > 0 ? "" : "NONE");
- for (int m = 0; m < heads().length(); m++) {
- tty->print("%6d ", m); heads().at(m)->dump();
- tty->print(" "); tails().at(m)->dump();
- }
-}
-#endif
-
// Get all memory nodes of a slice, in reverse order
void VLoopMemorySlices::get_slice_in_reverse_order(PhiNode* head, MemNode* tail, GrowableArray &slice) const {
+ assert(head != nullptr && tail != nullptr, "must be slice with memory state loop");
assert(slice.is_empty(), "start empty");
Node* n = tail;
Node* prev = nullptr;
@@ -1576,7 +1551,7 @@ void SuperWord::filter_packs_for_alignment() {
MemNode const* mem = current->as_constrained()->mem_ref();
Node_List* pack = get_pack(mem);
assert(pack != nullptr, "memop of final solution must still be packed");
- _mem_ref_for_main_loop_alignment = mem;
+ _vpointer_for_main_loop_alignment = &vpointer(mem);
_aw_for_main_loop_alignment = pack->size() * mem->memory_size();
}
}
@@ -1946,7 +1921,9 @@ void PackSet::verify() const {
}
#endif
-bool SuperWord::schedule_and_apply() const {
+// Build VTransform from SuperWord Packset, and eventually apply it (create new vectorized C2 loop).
+// See description at top of "vtransform.hpp".
+bool SuperWord::do_vtransform() const {
if (_packset.is_empty()) { return false; }
// Make an empty transform.
@@ -1959,7 +1936,7 @@ bool SuperWord::schedule_and_apply() const {
is_trace_superword_info());
#endif
VTransform vtransform(_vloop_analyzer,
- _mem_ref_for_main_loop_alignment,
+ _vpointer_for_main_loop_alignment,
_aw_for_main_loop_alignment
NOT_PRODUCT(COMMA trace)
);
@@ -1988,6 +1965,7 @@ bool SuperWord::schedule_and_apply() const {
// Apply the vectorization, i.e. we irreversibly edit the C2 graph. At this point, all
// correctness and profitability checks have passed, and the graph was successfully scheduled.
+// See description at top of "vtransform.hpp".
void VTransform::apply() {
#ifndef PRODUCT
if (_trace._info || TraceLoopOpts) {
@@ -2002,9 +1980,6 @@ void VTransform::apply() {
Compile* C = phase()->C;
C->print_method(PHASE_AUTO_VECTORIZATION1_BEFORE_APPLY, 4, cl());
- _graph.apply_memops_reordering_with_schedule();
- C->print_method(PHASE_AUTO_VECTORIZATION2_AFTER_REORDER, 4, cl());
-
adjust_pre_loop_limit_to_align_main_loop_vectors();
C->print_method(PHASE_AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT, 4, cl());
@@ -2016,102 +1991,11 @@ void VTransform::apply() {
C->print_method(PHASE_AUTO_VECTORIZATION5_AFTER_APPLY, 4, cl());
}
-// We prepare the memory graph for the replacement of scalar memops with vector memops.
-// We reorder all slices in parallel, ensuring that the memops inside each slice are
-// ordered according to the _schedule. This means that all packed memops are consecutive
-// in the memory graph after the reordering.
-void VTransformGraph::apply_memops_reordering_with_schedule() const {
-#ifndef PRODUCT
- assert(is_scheduled(), "must be already scheduled");
- if (_trace._info) {
- print_memops_schedule();
- }
-#endif
-
- ResourceMark rm;
- int max_slices = phase()->C->num_alias_types();
- // When iterating over the schedule, we keep track of the current memory state,
- // which is the Phi or a store in the loop.
- GrowableArray current_state_in_slice(max_slices, max_slices, nullptr);
- // The memory state after the loop is the last store inside the loop. If we reorder the
- // loop we may have a different last store, and we need to adjust the uses accordingly.
- GrowableArray old_last_store_in_slice(max_slices, max_slices, nullptr);
-
- const GrowableArray& mem_slice_head = _vloop_analyzer.memory_slices().heads();
-
- // (1) Set up the initial memory state from Phi. And find the old last store.
- for (int i = 0; i < mem_slice_head.length(); i++) {
- Node* phi = mem_slice_head.at(i);
- assert(phi->is_Phi(), "must be phi");
- int alias_idx = phase()->C->get_alias_index(phi->adr_type());
- current_state_in_slice.at_put(alias_idx, phi);
-
- // If we have a memory phi, we have a last store in the loop, find it over backedge.
- StoreNode* last_store = phi->in(2)->as_Store();
- old_last_store_in_slice.at_put(alias_idx, last_store);
- }
-
- // (2) Walk over schedule, append memops to the current state
- // of that slice. If it is a Store, we take it as the new state.
- for_each_memop_in_schedule([&] (MemNode* n) {
- assert(n->is_Load() || n->is_Store(), "only loads or stores");
- int alias_idx = phase()->C->get_alias_index(n->adr_type());
- Node* current_state = current_state_in_slice.at(alias_idx);
- if (current_state == nullptr) {
- // If there are only loads in a slice, we never update the memory
- // state in the loop, hence there is no phi for the memory state.
- // We just keep the old memory state that was outside the loop.
- assert(n->is_Load() && !in_bb(n->in(MemNode::Memory)),
- "only loads can have memory state from outside loop");
- } else {
- igvn().replace_input_of(n, MemNode::Memory, current_state);
- if (n->is_Store()) {
- current_state_in_slice.at_put(alias_idx, n);
- }
- }
- });
-
- // (3) For each slice, we add the current state to the backedge
- // in the Phi. Further, we replace uses of the old last store
- // with uses of the new last store (current_state).
- GrowableArray uses_after_loop;
- for (int i = 0; i < mem_slice_head.length(); i++) {
- Node* phi = mem_slice_head.at(i);
- int alias_idx = phase()->C->get_alias_index(phi->adr_type());
- Node* current_state = current_state_in_slice.at(alias_idx);
- assert(current_state != nullptr, "slice is mapped");
- assert(current_state != phi, "did some work in between");
- assert(current_state->is_Store(), "sanity");
- igvn().replace_input_of(phi, 2, current_state);
-
- // Replace uses of old last store with current_state (new last store)
- // Do it in two loops: first find all the uses, and change the graph
- // in as second loop so that we do not break the iterator.
- Node* last_store = old_last_store_in_slice.at(alias_idx);
- assert(last_store != nullptr, "we have a old last store");
- uses_after_loop.clear();
- for (DUIterator_Fast kmax, k = last_store->fast_outs(kmax); k < kmax; k++) {
- Node* use = last_store->fast_out(k);
- if (!in_bb(use)) {
- uses_after_loop.push(use);
- }
- }
- for (int k = 0; k < uses_after_loop.length(); k++) {
- Node* use = uses_after_loop.at(k);
- for (uint j = 0; j < use->req(); j++) {
- Node* def = use->in(j);
- if (def == last_store) {
- igvn().replace_input_of(use, j, current_state);
- }
- }
- }
- }
-}
-
void VTransformGraph::apply_vectorization_for_each_vtnode(uint& max_vector_length, uint& max_vector_width) const {
ResourceMark rm;
VTransformApplyState apply_state(_vloop_analyzer, _vtnodes.length());
+ // Apply: transform the node and connect with inputs (no backedges).
for (int i = 0; i < _schedule.length(); i++) {
VTransformNode* vtn = _schedule.at(i);
VTransformApplyResult result = vtn->apply(apply_state);
@@ -2121,6 +2005,16 @@ void VTransformGraph::apply_vectorization_for_each_vtnode(uint& max_vector_lengt
max_vector_length = MAX2(max_vector_length, result.vector_length());
max_vector_width = MAX2(max_vector_width, result.vector_width());
}
+
+ // Cleanup: connect backedges
+ for (int i = 0; i < _schedule.length(); i++) {
+ VTransformNode* vtn = _schedule.at(i);
+ vtn->apply_backedge(apply_state);
+ }
+
+ // Memory uses after the loop: used to connect to old last store,
+ // now need to connect to new last store.
+ apply_state.fix_memory_state_uses_after_loop();
}
// We call "apply" on every VTransformNode, which replaces the packed scalar nodes with vector nodes.
@@ -2774,10 +2668,10 @@ bool VLoopMemorySlices::same_memory_slice(MemNode* m1, MemNode* m2) const {
_vloop.phase()->C->get_alias_index(m2->adr_type());
}
-LoadNode::ControlDependency VTransformLoadVectorNode::control_dependency() const {
+LoadNode::ControlDependency SuperWordVTransformBuilder::load_control_dependency(const Node_List* pack) const {
LoadNode::ControlDependency dep = LoadNode::DependsOnlyOnTest;
- for (int i = 0; i < nodes().length(); i++) {
- Node* n = nodes().at(i);
+ for (uint i = 0; i < pack->size(); i++) {
+ Node* n = pack->at(i);
assert(n->is_Load(), "only meaningful for loads");
if (!n->depends_only_on_test()) {
if (n->as_Load()->has_unknown_control_dependency() &&
@@ -2795,22 +2689,24 @@ LoadNode::ControlDependency VTransformLoadVectorNode::control_dependency() const
// Find the memop pack with the maximum vector width, unless they were already
// determined (e.g. by SuperWord::filter_packs_for_alignment()).
-void VTransform::determine_mem_ref_and_aw_for_main_loop_alignment() {
- if (_mem_ref_for_main_loop_alignment != nullptr) {
- assert(VLoop::vectors_should_be_aligned(), "mem_ref only set if filtered for alignment");
+void VTransform::determine_vpointer_and_aw_for_main_loop_alignment() {
+ if (_vpointer_for_main_loop_alignment != nullptr) {
+ assert(VLoop::vectors_should_be_aligned(), "vpointer_for_main_loop_alignment only set if filtered for alignment");
return;
}
- MemNode const* mem_ref = nullptr;
+ VPointer const* vpointer = nullptr;
int max_aw = 0;
+ bool vpointer_is_load = false;
const GrowableArray& vtnodes = _graph.vtnodes();
for (int i = 0; i < vtnodes.length(); i++) {
VTransformMemVectorNode* vtn = vtnodes.at(i)->isa_MemVector();
if (vtn == nullptr) { continue; }
- MemNode* p0 = vtn->nodes().at(0)->as_Mem();
- int vw = p0->memory_size() * vtn->nodes().length();
+ int vw = vtn->vpointer().size();
+ bool vtn_is_load = vtn->is_load_in_loop();
+
// Generally, we prefer to align with the largest memory op (load or store).
// If there are multiple, then SuperWordAutomaticAlignment determines if we
// prefer loads or stores.
@@ -2820,15 +2716,16 @@ void VTransform::determine_mem_ref_and_aw_for_main_loop_alignment() {
// it is worse if a store is split, and less bad if a load is split.
// By default, we have SuperWordAutomaticAlignment=1, i.e. we align with a
// store if possible, to avoid splitting that store.
- bool prefer_store = mem_ref != nullptr && SuperWordAutomaticAlignment == 1 && mem_ref->is_Load() && p0->is_Store();
- bool prefer_load = mem_ref != nullptr && SuperWordAutomaticAlignment == 2 && mem_ref->is_Store() && p0->is_Load();
+ bool prefer_store = SuperWordAutomaticAlignment == 1 && vpointer_is_load && !vtn_is_load;
+ bool prefer_load = SuperWordAutomaticAlignment == 2 && !vpointer_is_load && vtn_is_load;
if (vw > max_aw || (vw == max_aw && (prefer_load || prefer_store))) {
+ vpointer = &vtn->vpointer();
max_aw = vw;
- mem_ref = p0;
+ vpointer_is_load = vtn_is_load;
}
}
- assert(mem_ref != nullptr && max_aw > 0, "found mem_ref and aw");
- _mem_ref_for_main_loop_alignment = mem_ref;
+ assert(vpointer != nullptr && max_aw > 0, "found vpointer and aw");
+ _vpointer_for_main_loop_alignment = vpointer;
_aw_for_main_loop_alignment = max_aw;
}
@@ -2842,13 +2739,17 @@ void VTransform::determine_mem_ref_and_aw_for_main_loop_alignment() {
} \
// Ensure that the main loop vectors are aligned by adjusting the pre loop limit. We memory-align
-// the address of "_mem_ref_for_main_loop_alignment" to "_aw_for_main_loop_alignment", which is a
+// the address of "_vpointer_for_main_loop_alignment" to "_aw_for_main_loop_alignment", which is a
// sufficiently large alignment width. We adjust the pre-loop iteration count by adjusting the
// pre-loop limit.
void VTransform::adjust_pre_loop_limit_to_align_main_loop_vectors() {
- determine_mem_ref_and_aw_for_main_loop_alignment();
- const MemNode* align_to_ref = _mem_ref_for_main_loop_alignment;
- const int aw = _aw_for_main_loop_alignment;
+ determine_vpointer_and_aw_for_main_loop_alignment();
+
+ assert(cl()->is_main_loop(), "can only do alignment for main loop");
+ assert(_vpointer_for_main_loop_alignment != nullptr &&
+ _vpointer_for_main_loop_alignment->is_valid() &&
+ _aw_for_main_loop_alignment > 0,
+ "must have alignment reference and aw");
if (!VLoop::vectors_should_be_aligned() && SuperWordAutomaticAlignment == 0) {
#ifdef ASSERT
@@ -2859,8 +2760,8 @@ void VTransform::adjust_pre_loop_limit_to_align_main_loop_vectors() {
return;
}
- assert(align_to_ref != nullptr && aw > 0, "must have alignment reference and aw");
- assert(cl()->is_main_loop(), "can only do alignment for main loop");
+ const VPointer& p = *_vpointer_for_main_loop_alignment;
+ const int aw = _aw_for_main_loop_alignment;
// The opaque node for the limit, where we adjust the input
Opaque1Node* pre_opaq = _vloop.pre_loop_end()->limit()->as_Opaque1();
@@ -2875,10 +2776,7 @@ void VTransform::adjust_pre_loop_limit_to_align_main_loop_vectors() {
Node* orig_limit = pre_opaq->original_loop_limit();
assert(orig_limit != nullptr && igvn().type(orig_limit) != Type::TOP, "");
- const VPointer& p = vpointer(align_to_ref);
- assert(p.is_valid(), "sanity");
-
- // For the main-loop, we want the address of align_to_ref to be memory aligned
+ // For the main-loop, we want the address of vpointer p to be memory aligned
// with some alignment width (aw, a power of 2). When we enter the main-loop,
// we know that iv is equal to the pre-loop limit. If we adjust the pre-loop
// limit by executing adjust_pre_iter many extra iterations, we can change the
@@ -3013,9 +2911,7 @@ void VTransform::adjust_pre_loop_limit_to_align_main_loop_vectors() {
#ifdef ASSERT
if (_trace._align_vector) {
tty->print_cr("\nVTransform::adjust_pre_loop_limit_to_align_main_loop_vectors:");
- tty->print(" align_to_ref:");
- align_to_ref->dump();
- tty->print(" ");
+ tty->print(" vpointer_for_main_loop_alignment");
p.print_on(tty);
tty->print_cr(" aw: %d", aw);
tty->print_cr(" iv_stride: %d", iv_stride);
diff --git a/src/hotspot/share/opto/superword.hpp b/src/hotspot/share/opto/superword.hpp
index 0940e752f85..118e0aa042c 100644
--- a/src/hotspot/share/opto/superword.hpp
+++ b/src/hotspot/share/opto/superword.hpp
@@ -410,9 +410,9 @@ class SuperWord : public ResourceObj {
PairSet _pairset;
PackSet _packset;
- // Memory reference, and the alignment width (aw) for which we align the main-loop,
+ // VPointer, and the alignment width (aw) for which we align the main-loop,
// by adjusting the pre-loop limit.
- MemNode const* _mem_ref_for_main_loop_alignment;
+ VPointer const* _vpointer_for_main_loop_alignment;
int _aw_for_main_loop_alignment;
public:
@@ -657,7 +657,7 @@ private:
bool is_velt_basic_type_compatible_use_def(Node* use, Node* def) const;
- bool schedule_and_apply() const;
+ bool do_vtransform() const;
};
#endif // SHARE_OPTO_SUPERWORD_HPP
diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.cpp b/src/hotspot/share/opto/superwordVTransformBuilder.cpp
index dbc96c234a9..45c919ccffa 100644
--- a/src/hotspot/share/opto/superwordVTransformBuilder.cpp
+++ b/src/hotspot/share/opto/superwordVTransformBuilder.cpp
@@ -37,6 +37,10 @@ void SuperWordVTransformBuilder::build() {
VectorSet vtn_memory_dependencies; // Shared, but cleared for every vtnode.
build_inputs_for_vector_vtnodes(vtn_memory_dependencies);
build_inputs_for_scalar_vtnodes(vtn_memory_dependencies);
+
+ // Build vtnodes for all uses of nodes from the loop, and connect them
+ // as outputs to the nodes in the loop.
+ build_uses_after_loop();
}
void SuperWordVTransformBuilder::build_vector_vtnodes_for_packed_nodes() {
@@ -50,8 +54,8 @@ void SuperWordVTransformBuilder::build_vector_vtnodes_for_packed_nodes() {
}
void SuperWordVTransformBuilder::build_scalar_vtnodes_for_non_packed_nodes() {
- for (int i = 0; i < _vloop_analyzer.body().body().length(); i++) {
- Node* n = _vloop_analyzer.body().body().at(i);
+ for (uint i = 0; i < _vloop.lpt()->_body.size(); i++) {
+ Node* n = _vloop.lpt()->_body.at(i);
if (_packset.get_pack(n) != nullptr) { continue; }
VTransformNode* vtn = nullptr;
@@ -61,6 +65,8 @@ void SuperWordVTransformBuilder::build_scalar_vtnodes_for_non_packed_nodes() {
vtn = new (_vtransform.arena()) VTransformMemopScalarNode(_vtransform, mem, mem_p);
} else if (n->is_Phi()) {
vtn = new (_vtransform.arena()) VTransformLoopPhiNode(_vtransform, n->as_Phi());
+ } else if (n->is_CountedLoop()) {
+ vtn = new (_vtransform.arena()) VTransformCountedLoopNode(_vtransform, n->as_CountedLoop());
} else if (n->is_CFG()) {
vtn = new (_vtransform.arena()) VTransformCFGNode(_vtransform, n);
} else {
@@ -121,8 +127,8 @@ void SuperWordVTransformBuilder::build_inputs_for_vector_vtnodes(VectorSet& vtn_
}
void SuperWordVTransformBuilder::build_inputs_for_scalar_vtnodes(VectorSet& vtn_memory_dependencies) {
- for (int i = 0; i < _vloop_analyzer.body().body().length(); i++) {
- Node* n = _vloop_analyzer.body().body().at(i);
+ for (uint i = 0; i < _vloop.lpt()->_body.size(); i++) {
+ Node* n = _vloop.lpt()->_body.at(i);
VTransformNode* vtn = get_vtnode(n);
if (vtn->isa_Vector() != nullptr) { continue; }
vtn_memory_dependencies.clear(); // Add every dependency only once per vtn.
@@ -135,18 +141,41 @@ void SuperWordVTransformBuilder::build_inputs_for_scalar_vtnodes(VectorSet& vtn_
init_req_with_scalar(n, vtn, MemNode::ValueIn);
add_memory_dependencies_of_node_to_vtnode(n, vtn, vtn_memory_dependencies);
} else if (n->is_CountedLoop()) {
- continue; // Is "root", has no dependency.
- } else if (n->is_Phi()) {
- // CountedLoop Phi's: ignore backedge (and entry value).
- assert(n->in(0) == _vloop.cl(), "only Phi's from the CountedLoop allowed");
- init_req_with_scalar(n, vtn, 0);
- continue;
+ // Avoid self-loop, it only creates unnecessary issues in scheduling.
+ init_req_with_scalar(n, vtn, LoopNode::EntryControl);
+ init_req_with_scalar(n, vtn, LoopNode::LoopBackControl);
} else {
init_all_req_with_scalars(n, vtn);
}
}
}
+// Build vtnodes for all uses of nodes from the loop, and connect them
+// as outputs to the nodes in the loop.
+void SuperWordVTransformBuilder::build_uses_after_loop() {
+ for (uint i = 0; i < _vloop.lpt()->_body.size(); i++) {
+ Node* n = _vloop.lpt()->_body.at(i);
+ VTransformNode* vtn = get_vtnode(n);
+
+ for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
+ Node* use = n->fast_out(i);
+
+ if (!_vloop.in_bb(use)) {
+ VTransformNode* vtn_use = get_vtnode_or_wrap_as_outer(use);
+
+ // Set all edges
+ for (uint j = 0; j < use->req(); j++) {
+ Node* def = use->in(j);
+ if (n == def && vtn_use->in_req(j) != vtn) {
+ assert(vtn_use->in_req(j) == nullptr, "should not yet be set");
+ vtn_use->init_req(j, vtn);
+ }
+ }
+ }
+ }
+ }
+}
+
// Create a vtnode for each pack. No in/out edges set yet.
VTransformVectorNode* SuperWordVTransformBuilder::make_vector_vtnode_for_pack(const Node_List* pack) const {
Node* p0 = pack->at(0);
@@ -159,7 +188,8 @@ VTransformVectorNode* SuperWordVTransformBuilder::make_vector_vtnode_for_pack(co
if (p0->is_Load()) {
const VPointer& scalar_p = _vloop_analyzer.vpointers().vpointer(p0->as_Load());
const VPointer vector_p(scalar_p.make_with_size(scalar_p.size() * vlen));
- vtn = new (_vtransform.arena()) VTransformLoadVectorNode(_vtransform, properties, vector_p, p0->adr_type());
+ const LoadNode::ControlDependency control_dependency = load_control_dependency(pack);
+ vtn = new (_vtransform.arena()) VTransformLoadVectorNode(_vtransform, properties, vector_p, p0->adr_type(), control_dependency);
} else if (p0->is_Store()) {
const VPointer& scalar_p = _vloop_analyzer.vpointers().vpointer(p0->as_Store());
const VPointer vector_p(scalar_p.make_with_size(scalar_p.size() * vlen));
@@ -209,7 +239,6 @@ VTransformVectorNode* SuperWordVTransformBuilder::make_vector_vtnode_for_pack(co
int vopc = VectorNode::opcode(sopc, bt);
vtn = new (_vtransform.arena()) VTransformElementWiseVectorNode(_vtransform, p0->req(), properties, vopc);
}
- vtn->set_nodes(pack);
return vtn;
}
@@ -276,15 +305,15 @@ VTransformNode* SuperWordVTransformBuilder::get_or_make_vtnode_vector_input_at_i
// case of a ConvL2I, it can be int or some narrower type such
// as short etc. But given we replicate the input of the Convert
// node, we have to use the input type instead.
- BasicType element_type = p0->is_Convert() ? p0->in(1)->bottom_type()->basic_type() : _vloop_analyzer.types().velt_basic_type(p0);
- if (index == 2 && VectorNode::is_scalar_rotate(p0) && element_type == T_LONG) {
+ BasicType element_bt = p0->is_Convert() ? p0->in(1)->bottom_type()->basic_type() : _vloop_analyzer.types().velt_basic_type(p0);
+ if (index == 2 && VectorNode::is_scalar_rotate(p0) && element_bt == T_LONG) {
// Scalar rotate has int rotation value, but the scalar rotate expects longs.
assert(same_input->bottom_type()->isa_int(), "scalar rotate expects int rotation");
VTransformNode* conv = new (_vtransform.arena()) VTransformConvI2LNode(_vtransform);
conv->init_req(1, same_input_vtn);
same_input_vtn = conv;
}
- VTransformNode* replicate = new (_vtransform.arena()) VTransformReplicateNode(_vtransform, pack->size(), element_type);
+ VTransformNode* replicate = new (_vtransform.arena()) VTransformReplicateNode(_vtransform, pack->size(), element_bt);
replicate->init_req(1, same_input_vtn);
return replicate;
}
@@ -307,6 +336,7 @@ VTransformNode* SuperWordVTransformBuilder::get_vtnode_or_wrap_as_outer(Node* n)
assert(!_vloop.in_bb(n), "only nodes outside the loop can be input nodes to the loop");
vtn = new (_vtransform.arena()) VTransformOuterNode(_vtransform, n);
map_node_to_vtnode(n, vtn);
+ assert(vtn == get_vtnode_or_null(n), "consistency");
return vtn;
}
diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.hpp b/src/hotspot/share/opto/superwordVTransformBuilder.hpp
index 6ed8480209a..cc9dd225f01 100644
--- a/src/hotspot/share/opto/superwordVTransformBuilder.hpp
+++ b/src/hotspot/share/opto/superwordVTransformBuilder.hpp
@@ -56,6 +56,7 @@ private:
void build_scalar_vtnodes_for_non_packed_nodes();
void build_inputs_for_vector_vtnodes(VectorSet& vtn_memory_dependencies);
void build_inputs_for_scalar_vtnodes(VectorSet& vtn_memory_dependencies);
+ void build_uses_after_loop();
// Helper methods for building VTransform.
VTransformNode* get_vtnode_or_null(Node* n) const {
@@ -82,6 +83,7 @@ private:
void init_all_req_with_scalars(Node* n, VTransformNode* vtn);
void init_all_req_with_vectors(const Node_List* pack, VTransformNode* vtn);
void add_memory_dependencies_of_node_to_vtnode(Node* n, VTransformNode* vtn, VectorSet& vtn_memory_dependencies);
+ LoadNode::ControlDependency load_control_dependency(const Node_List* pack) const;
};
#endif // SHARE_OPTO_SUPERWORD_VTRANSFORM_BUILDER_HPP
diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp
index 8e0f0980ff7..5c4e15fdbb9 100644
--- a/src/hotspot/share/opto/vectorization.cpp
+++ b/src/hotspot/share/opto/vectorization.cpp
@@ -182,6 +182,11 @@ VStatus VLoopAnalyzer::setup_submodules_helper() {
_reductions.mark_reductions();
}
+ VStatus body_status = _body.construct();
+ if (!body_status.is_success()) {
+ return body_status;
+ }
+
_memory_slices.find_memory_slices();
// If there is no memory slice detected, it means there is no store.
@@ -192,11 +197,6 @@ VStatus VLoopAnalyzer::setup_submodules_helper() {
return VStatus::make_failure(VLoopAnalyzer::FAILURE_NO_REDUCTION_OR_STORE);
}
- VStatus body_status = _body.construct();
- if (!body_status.is_success()) {
- return body_status;
- }
-
_types.compute_vector_element_type();
_vpointers.compute_vpointers();
@@ -206,6 +206,64 @@ VStatus VLoopAnalyzer::setup_submodules_helper() {
return VStatus::make_success();
}
+// There are 2 kinds of slices:
+// - No memory phi: only loads. All have the same input memory state from before the loop.
+// - With memory phi. Chain of memory operations inside the loop.
+void VLoopMemorySlices::find_memory_slices() {
+ Compile* C = _vloop.phase()->C;
+ // We iterate over the body, which is topologically sorted. Hence, if there is a phi
+ // in a slice, we will find it first, and the loads and stores afterwards.
+ for (int i = 0; i < _body.body().length(); i++) {
+ Node* n = _body.body().at(i);
+ if (n->is_memory_phi()) {
+ // Memory slice with stores (and maybe loads)
+ PhiNode* phi = n->as_Phi();
+ int alias_idx = C->get_alias_index(phi->adr_type());
+ assert(_inputs.at(alias_idx) == nullptr, "did not yet touch this slice");
+ _inputs.at_put(alias_idx, phi->in(1));
+ _heads.at_put(alias_idx, phi);
+ } else if (n->is_Load()) {
+ LoadNode* load = n->as_Load();
+ int alias_idx = C->get_alias_index(load->adr_type());
+ PhiNode* head = _heads.at(alias_idx);
+ if (head == nullptr) {
+ // We did not find a phi on this slice yet -> must be a slice with only loads.
+ assert(_inputs.at(alias_idx) == nullptr || _inputs.at(alias_idx) == load->in(1),
+ "not yet touched or the same input");
+ _inputs.at_put(alias_idx, load->in(1));
+ } // else: the load belongs to a slice with a phi that already set heads and inputs.
+#ifdef ASSERT
+ } else if (n->is_Store()) {
+ // Found a store. Make sure it is in a slice with a Phi.
+ StoreNode* store = n->as_Store();
+ int alias_idx = C->get_alias_index(store->adr_type());
+ PhiNode* head = _heads.at(alias_idx);
+ assert(head != nullptr, "should have found a mem phi for this slice");
+#endif
+ }
+ }
+ NOT_PRODUCT( if (_vloop.is_trace_memory_slices()) { print(); } )
+}
+
+#ifndef PRODUCT
+void VLoopMemorySlices::print() const {
+ tty->print_cr("\nVLoopMemorySlices::print: %s",
+ heads().length() > 0 ? "" : "NONE");
+ for (int i = 0; i < _inputs.length(); i++) {
+ Node* input = _inputs.at(i);
+ PhiNode* head = _heads.at(i);
+ if (input != nullptr) {
+ tty->print("%3d input", i); input->dump();
+ if (head == nullptr) {
+ tty->print_cr(" load only");
+ } else {
+ tty->print(" head "); head->dump();
+ }
+ }
+ }
+}
+#endif
+
void VLoopVPointers::compute_vpointers() {
count_vpointers();
allocate_vpointers_array();
@@ -267,7 +325,6 @@ void VLoopVPointers::print() const {
// the edge, i.e. spaw the order.
void VLoopDependencyGraph::construct() {
const GrowableArray& mem_slice_heads = _memory_slices.heads();
- const GrowableArray& mem_slice_tails = _memory_slices.tails();
ResourceMark rm;
GrowableArray slice_nodes;
@@ -277,7 +334,10 @@ void VLoopDependencyGraph::construct() {
// For each memory slice, create the memory subgraph
for (int i = 0; i < mem_slice_heads.length(); i++) {
PhiNode* head = mem_slice_heads.at(i);
- MemNode* tail = mem_slice_tails.at(i);
+ // If there is no head (memory-phi) for this slice, then we have either no memops
+ // in the loop, or only loads. We do not need to add any memory edges in that case.
+ if (head == nullptr) { continue; }
+ MemNode* tail = head->in(2)->as_Mem();
_memory_slices.get_slice_in_reverse_order(head, tail, slice_nodes);
diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp
index b39e46cbf35..e006589cce9 100644
--- a/src/hotspot/share/opto/vectorization.hpp
+++ b/src/hotspot/share/opto/vectorization.hpp
@@ -379,37 +379,6 @@ private:
static Node* original_input(const Node* n, uint i);
};
-// Submodule of VLoopAnalyzer.
-// Find the memory slices in the loop.
-class VLoopMemorySlices : public StackObj {
-private:
- const VLoop& _vloop;
-
- GrowableArray _heads;
- GrowableArray _tails;
-
-public:
- VLoopMemorySlices(Arena* arena, const VLoop& vloop) :
- _vloop(vloop),
- _heads(arena, 8, 0, nullptr),
- _tails(arena, 8, 0, nullptr) {};
- NONCOPYABLE(VLoopMemorySlices);
-
- void find_memory_slices();
-
- const GrowableArray& heads() const { return _heads; }
- const GrowableArray& tails() const { return _tails; }
-
- // Get all memory nodes of a slice, in reverse order
- void get_slice_in_reverse_order(PhiNode* head, MemNode* tail, GrowableArray& slice) const;
-
- bool same_memory_slice(MemNode* m1, MemNode* m2) const;
-
-#ifndef PRODUCT
- void print() const;
-#endif
-};
-
// Submodule of VLoopAnalyzer.
// Finds all nodes in the body, and creates a mapping node->_idx to a body_idx.
// This mapping is used so that subsequent datastructures sizes only grow with
@@ -461,6 +430,73 @@ private:
}
};
+// Submodule of VLoopAnalyzer.
+// Find the memory slices in the loop. There are 3 kinds of slices:
+// 1. no use in loop: inputs(i) = nullptr, heads(i) = nullptr
+// 2. stores in loop: inputs(i) = entry_mem, heads(i) = phi_mem
+//
+// = entry_mem
+// |
+// CountedLoop | +-----------------------+
+// | v v |
+// phi_mem |
+// | |
+// |
+// | |
+// +---------------------------+
+// |
+//
+//
+// Note: the mem uses after the loop are dependent on the last store in the loop.
+// Once we vectorize, we may reorder the loads and stores, and replace
+// scalar mem ops with vector mem ops. We will have to make sure that all
+// uses after the loop use the new last store.
+// See: VTransformApplyState::fix_memory_state_uses_after_loop
+//
+// 3. only loads but no stores in loop: inputs(i) = entry_mem, heads(i) = nullptr
+//
+// = entry_mem
+// | |
+// | CountedLoop |
+// | | |
+// |
+// |
+//
+//
+// Note: the mem uses after the loop are NOT dependent any mem ops in the loop,
+// since there are no stores.
+//
+class VLoopMemorySlices : public StackObj {
+private:
+ const VLoop& _vloop;
+ const VLoopBody& _body;
+
+ GrowableArray _inputs;
+ GrowableArray _heads;
+
+public:
+ VLoopMemorySlices(Arena* arena, const VLoop& vloop, const VLoopBody& body) :
+ _vloop(vloop),
+ _body(body),
+ _inputs(arena, num_slices(), num_slices(), nullptr),
+ _heads(arena, num_slices(), num_slices(), nullptr) {};
+ NONCOPYABLE(VLoopMemorySlices);
+
+ const GrowableArray& inputs() const { return _inputs; }
+ const GrowableArray& heads() const { return _heads; }
+
+ void find_memory_slices();
+ void get_slice_in_reverse_order(PhiNode* head, MemNode* tail, GrowableArray& slice) const;
+ bool same_memory_slice(MemNode* m1, MemNode* m2) const;
+
+private:
+#ifndef PRODUCT
+ void print() const;
+#endif
+
+ int num_slices() const { return _vloop.phase()->C->num_alias_types(); }
+};
+
// Submodule of VLoopAnalyzer.
// Compute the vector element type for every node in the loop body.
// We need to do this to be able to vectorize the narrower integer
@@ -737,8 +773,8 @@ private:
// Submodules
VLoopReductions _reductions;
- VLoopMemorySlices _memory_slices;
VLoopBody _body;
+ VLoopMemorySlices _memory_slices;
VLoopTypes _types;
VLoopVPointers _vpointers;
VLoopDependencyGraph _dependency_graph;
@@ -749,8 +785,8 @@ public:
_arena(mtCompiler, Arena::Tag::tag_superword),
_success(false),
_reductions (&_arena, vloop),
- _memory_slices (&_arena, vloop),
_body (&_arena, vloop, vshared),
+ _memory_slices (&_arena, vloop, _body),
_types (&_arena, vloop, _body),
_vpointers (&_arena, vloop, _body),
_dependency_graph(&_arena, vloop, _body, _memory_slices, _vpointers)
diff --git a/src/hotspot/share/opto/vtransform.cpp b/src/hotspot/share/opto/vtransform.cpp
index 8c1210a5a09..27c541c2732 100644
--- a/src/hotspot/share/opto/vtransform.cpp
+++ b/src/hotspot/share/opto/vtransform.cpp
@@ -78,6 +78,10 @@ bool VTransformGraph::schedule() {
// runtime check, see VTransform::apply_speculative_aliasing_runtime_checks.
for (uint i = 0; i < vtn->out_strong_edges(); i++) {
VTransformNode* use = vtn->out_strong_edge(i);
+
+ // Skip LoopPhi backedge.
+ if ((use->isa_LoopPhi() != nullptr || use->isa_CountedLoop() != nullptr) && use->in_req(2) == vtn) { continue; }
+
if (post_visited.test(use->_idx)) { continue; }
if (pre_visited.test(use->_idx)) {
// Cycle detected!
@@ -120,6 +124,11 @@ void VTransformGraph::collect_nodes_without_strong_in_edges(GrowableArrayhas_strong_in_edge()) {
stack.push(vtn);
}
+ // If an Outer node has both inputs and outputs, we will most likely have cycles in the final graph.
+ // This is not a correctness problem, but it just will prevent vectorization. If this ever happens
+ // try to find a way to avoid the cycle somehow.
+ assert(vtn->isa_Outer() == nullptr || (vtn->has_strong_in_edge() != (vtn->out_strong_edges() > 0)),
+ "Outer nodes should either be inputs or outputs, but not both, otherwise we may get cycles");
}
}
@@ -717,28 +726,111 @@ Node* VTransformApplyState::transformed_node(const VTransformNode* vtn) const {
return n;
}
+void VTransformApplyState::init_memory_states_and_uses_after_loop() {
+ const GrowableArray& inputs = _vloop_analyzer.memory_slices().inputs();
+ const GrowableArray& heads = _vloop_analyzer.memory_slices().heads();
+ for (int i = 0; i < inputs.length(); i++) {
+ PhiNode* head = heads.at(i);
+ if (head != nullptr) {
+ // Slice with Phi (i.e. with stores) -> start with the phi (phi_mem)
+ _memory_states.at_put(i, head);
+
+ // Remember uses outside the loop of the last memory state (store).
+ StoreNode* last_store = head->in(2)->as_Store();
+ assert(vloop().in_bb(last_store), "backedge store should be in the loop");
+ for (DUIterator_Fast jmax, j = last_store->fast_outs(jmax); j < jmax; j++) {
+ Node* use = last_store->fast_out(j);
+ if (!vloop().in_bb(use)) {
+ for (uint k = 0; k < use->req(); k++) {
+ if (use->in(k) == last_store) {
+ _memory_state_uses_after_loop.push(MemoryStateUseAfterLoop(use, k, i));
+ }
+ }
+ }
+ }
+ } else {
+ // Slice without Phi (i.e. only loads) -> use the input state (entry_mem)
+ _memory_states.at_put(i, inputs.at(i));
+ }
+ }
+}
+
+// We may have reordered the scalar stores, or replaced them with vectors. Now
+// the last memory state in the loop may have changed. Thus, we need to change
+// the uses of the old last memory state the new last memory state.
+void VTransformApplyState::fix_memory_state_uses_after_loop() {
+ for (int i = 0; i < _memory_state_uses_after_loop.length(); i++) {
+ MemoryStateUseAfterLoop& use = _memory_state_uses_after_loop.at(i);
+ Node* last_state = memory_state(use._alias_idx);
+ phase()->igvn().replace_input_of(use._use, use._in_idx, last_state);
+ }
+}
+
+void VTransformNode::apply_vtn_inputs_to_node(Node* n, VTransformApplyState& apply_state) const {
+ PhaseIdealLoop* phase = apply_state.phase();
+ for (uint i = 0; i < req(); i++) {
+ VTransformNode* vtn_def = in_req(i);
+ if (vtn_def != nullptr) {
+ Node* def = apply_state.transformed_node(vtn_def);
+ phase->igvn().replace_input_of(n, i, def);
+ }
+ }
+}
+
VTransformApplyResult VTransformMemopScalarNode::apply(VTransformApplyState& apply_state) const {
- // This was just wrapped. Now we simply unwrap without touching the inputs.
+ apply_vtn_inputs_to_node(_node, apply_state);
+ // The memory state has to be applied separately: the vtn does not hold it. This allows reordering.
+ Node* mem = apply_state.memory_state(_node->adr_type());
+ apply_state.phase()->igvn().replace_input_of(_node, 1, mem);
+ if (_node->is_Store()) {
+ apply_state.set_memory_state(_node->adr_type(), _node);
+ }
+
return VTransformApplyResult::make_scalar(_node);
}
VTransformApplyResult VTransformDataScalarNode::apply(VTransformApplyState& apply_state) const {
- // This was just wrapped. Now we simply unwrap without touching the inputs.
+ apply_vtn_inputs_to_node(_node, apply_state);
return VTransformApplyResult::make_scalar(_node);
}
VTransformApplyResult VTransformLoopPhiNode::apply(VTransformApplyState& apply_state) const {
- // This was just wrapped. Now we simply unwrap without touching the inputs.
+ PhaseIdealLoop* phase = apply_state.phase();
+ Node* in0 = apply_state.transformed_node(in_req(0));
+ Node* in1 = apply_state.transformed_node(in_req(1));
+ phase->igvn().replace_input_of(_node, 0, in0);
+ phase->igvn().replace_input_of(_node, 1, in1);
+ // Note: the backedge is hooked up later.
return VTransformApplyResult::make_scalar(_node);
}
+// Cleanup backedges. In the schedule, the backedges come after their phis. Hence,
+// we only have the transformed backedges after the phis are already transformed.
+// We hook the backedges into the phis now, during cleanup.
+void VTransformLoopPhiNode::apply_backedge(VTransformApplyState& apply_state) const {
+ PhaseIdealLoop* phase = apply_state.phase();
+ if (_node->is_memory_phi()) {
+ // Memory phi/backedge
+ // The last memory state of that slice is the backedge.
+ Node* last_state = apply_state.memory_state(_node->adr_type());
+ phase->igvn().replace_input_of(_node, 2, last_state);
+ } else {
+ // Data phi/backedge
+ Node* in2 = apply_state.transformed_node(in_req(2));
+ phase->igvn().replace_input_of(_node, 2, in2);
+ }
+}
+
VTransformApplyResult VTransformCFGNode::apply(VTransformApplyState& apply_state) const {
- // This was just wrapped. Now we simply unwrap without touching the inputs.
+ // We do not modify the inputs of the CountedLoop (and certainly not its backedge)
+ if (!_node->is_CountedLoop()) {
+ apply_vtn_inputs_to_node(_node, apply_state);
+ }
return VTransformApplyResult::make_scalar(_node);
}
VTransformApplyResult VTransformOuterNode::apply(VTransformApplyState& apply_state) const {
- // This was just wrapped. Now we simply unwrap without touching the inputs.
+ apply_vtn_inputs_to_node(_node, apply_state);
return VTransformApplyResult::make_scalar(_node);
}
@@ -797,7 +889,7 @@ VTransformApplyResult VTransformElementWiseVectorNode::apply(VTransformApplyStat
vn = VectorNode::make(_vector_opcode, in1, in2, in3, vt); // ternary
}
- register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn);
+ register_new_node_from_vectorization(apply_state, vn);
return VTransformApplyResult::make_vector(vn);
}
@@ -812,7 +904,7 @@ VTransformApplyResult VTransformElementWiseLongOpWithCastToIntVectorNode::apply(
register_new_node_from_vectorization(apply_state, long_vn);
// Cast long -> int, to mimic the scalar long -> int operation.
VectorNode* vn = VectorCastNode::make(Op_VectorCastL2X, long_vn, T_INT, vlen);
- register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn);
+ register_new_node_from_vectorization(apply_state, vn);
return VTransformApplyResult::make_vector(vn);
}
@@ -824,7 +916,7 @@ VTransformApplyResult VTransformReinterpretVectorNode::apply(VTransformApplyStat
Node* in1 = apply_state.transformed_node(in_req(1));
VectorNode* vn = new VectorReinterpretNode(in1, src_vt, dst_vt);
- register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn);
+ register_new_node_from_vectorization(apply_state, vn);
return VTransformApplyResult::make_vector(vn);
}
@@ -843,7 +935,7 @@ VTransformApplyResult VTransformBoolVectorNode::apply(VTransformApplyState& appl
PhaseIdealLoop* phase = apply_state.phase();
ConINode* mask_node = phase->intcon((int)mask);
VectorNode* vn = new VectorMaskCmpNode(mask, cmp_in1, cmp_in2, mask_node, vt);
- register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn);
+ register_new_node_from_vectorization(apply_state, vn);
return VTransformApplyResult::make_vector(vn);
}
@@ -852,7 +944,7 @@ VTransformApplyResult VTransformReductionVectorNode::apply(VTransformApplyState&
Node* vec = apply_state.transformed_node(in_req(2));
ReductionNode* vn = ReductionNode::make(scalar_opcode(), nullptr, init, vec, element_basic_type());
- register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn);
+ register_new_node_from_vectorization(apply_state, vn);
return VTransformApplyResult::make_vector(vn, vn->vect_type());
}
@@ -861,10 +953,9 @@ VTransformApplyResult VTransformLoadVectorNode::apply(VTransformApplyState& appl
uint vlen = vector_length();
BasicType bt = element_basic_type();
- LoadNode* first = nodes().at(0)->as_Load();
+ // The memory state has to be applied separately: the vtn does not hold it. This allows reordering.
Node* ctrl = apply_state.transformed_node(in_req(MemNode::Control));
- // first has the correct memory state, determined by VTransformGraph::apply_memops_reordering_with_schedule
- Node* mem = first->in(MemNode::Memory);
+ Node* mem = apply_state.memory_state(_adr_type);
Node* adr = apply_state.transformed_node(in_req(MemNode::Address));
// Set the memory dependency of the LoadVector as early as possible.
@@ -880,10 +971,9 @@ VTransformApplyResult VTransformLoadVectorNode::apply(VTransformApplyState& appl
}
}
- LoadVectorNode* vn = LoadVectorNode::make(sopc, ctrl, mem, adr, _adr_type, vlen, bt,
- control_dependency());
+ LoadVectorNode* vn = LoadVectorNode::make(sopc, ctrl, mem, adr, _adr_type, vlen, bt, _control_dependency);
DEBUG_ONLY( if (VerifyAlignVector) { vn->set_must_verify_alignment(); } )
- register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn);
+ register_new_node_from_vectorization(apply_state, vn);
return VTransformApplyResult::make_vector(vn, vn->vect_type());
}
@@ -891,27 +981,17 @@ VTransformApplyResult VTransformStoreVectorNode::apply(VTransformApplyState& app
int sopc = scalar_opcode();
uint vlen = vector_length();
- StoreNode* first = nodes().at(0)->as_Store();
+ // The memory state has to be applied separately: the vtn does not hold it. This allows reordering.
Node* ctrl = apply_state.transformed_node(in_req(MemNode::Control));
- // first has the correct memory state, determined by VTransformGraph::apply_memops_reordering_with_schedule
- Node* mem = first->in(MemNode::Memory);
+ Node* mem = apply_state.memory_state(_adr_type);
Node* adr = apply_state.transformed_node(in_req(MemNode::Address));
Node* value = apply_state.transformed_node(in_req(MemNode::ValueIn));
StoreVectorNode* vn = StoreVectorNode::make(sopc, ctrl, mem, adr, _adr_type, value, vlen);
DEBUG_ONLY( if (VerifyAlignVector) { vn->set_must_verify_alignment(); } )
- register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn);
- return VTransformApplyResult::make_vector(vn, vn->vect_type());
-}
-
-void VTransformVectorNode::register_new_node_from_vectorization_and_replace_scalar_nodes(VTransformApplyState& apply_state, Node* vn) const {
- PhaseIdealLoop* phase = apply_state.phase();
register_new_node_from_vectorization(apply_state, vn);
-
- for (int i = 0; i < _nodes.length(); i++) {
- Node* n = _nodes.at(i);
- phase->igvn().replace_node(n, vn);
- }
+ apply_state.set_memory_state(_adr_type, vn);
+ return VTransformApplyResult::make_vector(vn, vn->vect_type());
}
void VTransformNode::register_new_node_from_vectorization(VTransformApplyState& apply_state, Node* vn) const {
@@ -944,15 +1024,6 @@ void VTransformGraph::print_schedule() const {
}
}
-void VTransformGraph::print_memops_schedule() const {
- tty->print_cr("\nVTransformGraph::print_memops_schedule:");
- int i = 0;
- for_each_memop_in_schedule([&] (MemNode* mem) {
- tty->print(" %3d: ", i++);
- mem->dump();
- });
-}
-
void VTransformNode::print() const {
tty->print("%3d %s (", _idx, name());
for (uint i = 0; i < _req; i++) {
diff --git a/src/hotspot/share/opto/vtransform.hpp b/src/hotspot/share/opto/vtransform.hpp
index 9a4e4de01a2..a004962eea7 100644
--- a/src/hotspot/share/opto/vtransform.hpp
+++ b/src/hotspot/share/opto/vtransform.hpp
@@ -39,7 +39,7 @@
//
// This is the life-cycle of a VTransform:
// - Construction:
-// - From SuperWord, with the SuperWordVTransformBuilder.
+// - From SuperWord PackSet, with the SuperWordVTransformBuilder.
//
// - Future Plans: optimize, if-conversion, etc.
//
@@ -49,8 +49,16 @@
//
// - Apply:
// - Changes to the C2 IR are only made once the "apply" method is called.
+// - Align the main loop, by adjusting pre loop limit.
+// - Add speculative runtime checks (alignment and aliasing).
// - Each vtnode generates its corresponding scalar and vector C2 nodes,
-// possibly replacing old scalar C2 nodes.
+// possibly replacing old scalar C2 nodes. We apply each vtnode in order
+// of the schedule, so that all input vtnodes are already applied, i.e.
+// all input vtnodes have already generated the transformed C2 nodes.
+// - We also build the new memory graph on the fly. The schedule may have
+// reordered the memory operations, and so we cannot use the old memory
+// graph, but must build it from the scheduled order. We keep track of
+// the current memory state in VTransformApplyState.
//
// Future Plans with VTransform:
// - Cost model: estimate if vectorization is profitable.
@@ -65,6 +73,7 @@ class VTransformMemopScalarNode;
class VTransformDataScalarNode;
class VTransformLoopPhiNode;
class VTransformCFGNode;
+class VTransformCountedLoopNode;
class VTransformOuterNode;
class VTransformVectorNode;
class VTransformElementWiseVectorNode;
@@ -176,7 +185,6 @@ public:
bool schedule();
bool has_store_to_load_forwarding_failure(const VLoopAnalyzer& vloop_analyzer) const;
- void apply_memops_reordering_with_schedule() const;
void apply_vectorization_for_each_vtnode(uint& max_vector_length, uint& max_vector_width) const;
private:
@@ -187,13 +195,9 @@ private:
void collect_nodes_without_strong_in_edges(GrowableArray& stack) const;
- template
- void for_each_memop_in_schedule(Callback callback) const;
-
#ifndef PRODUCT
void print_vtnodes() const;
void print_schedule() const;
- void print_memops_schedule() const;
void trace_schedule_cycle(const GrowableArray& stack,
const VectorSet& pre_visited,
const VectorSet& post_visited) const;
@@ -215,14 +219,14 @@ private:
VTransformGraph _graph;
- // Memory reference, and the alignment width (aw) for which we align the main-loop,
+ // VPointer, and the alignment width (aw) for which we align the main-loop,
// by adjusting the pre-loop limit.
- MemNode const* _mem_ref_for_main_loop_alignment;
+ VPointer const* _vpointer_for_main_loop_alignment;
int _aw_for_main_loop_alignment;
public:
VTransform(const VLoopAnalyzer& vloop_analyzer,
- MemNode const* mem_ref_for_main_loop_alignment,
+ VPointer const* vpointer_for_main_loop_alignment,
int aw_for_main_loop_alignment
NOT_PRODUCT( COMMA const VTransformTrace trace)
) :
@@ -231,7 +235,7 @@ public:
NOT_PRODUCT(_trace(trace) COMMA)
_arena(mtCompiler, Arena::Tag::tag_superword),
_graph(_vloop_analyzer, _arena NOT_PRODUCT(COMMA _trace)),
- _mem_ref_for_main_loop_alignment(mem_ref_for_main_loop_alignment),
+ _vpointer_for_main_loop_alignment(vpointer_for_main_loop_alignment),
_aw_for_main_loop_alignment(aw_for_main_loop_alignment) {}
const VLoopAnalyzer& vloop_analyzer() const { return _vloop_analyzer; }
@@ -257,7 +261,7 @@ private:
}
// Ensure that the main loop vectors are aligned by adjusting the pre loop limit.
- void determine_mem_ref_and_aw_for_main_loop_alignment();
+ void determine_vpointer_and_aw_for_main_loop_alignment();
void adjust_pre_loop_limit_to_align_main_loop_vectors();
void apply_speculative_alignment_runtime_checks();
@@ -271,7 +275,7 @@ private:
};
// Keeps track of the state during "VTransform::apply"
-// -> keep track of the already transformed nodes
+// -> keep track of the already transformed nodes and the memory state.
class VTransformApplyState : public StackObj {
private:
const VLoopAnalyzer& _vloop_analyzer;
@@ -281,11 +285,35 @@ private:
// generated def (input) nodes when we are generating the use nodes in "apply".
GrowableArray _vtnode_idx_to_transformed_node;
+ // We keep track of the current memory state in each slice. If the slice has only
+ // loads (and no phi), then this is always the input memory state from before the
+ // loop. If there is a memory phi, this is initially the memory phi, and each time
+ // a store is processed, it is updated to that store.
+ GrowableArray _memory_states;
+
+ // We need to keep track of the memory uses after the loop, for the slices that
+ // have a memory phi.
+ // use->in(in_idx) =
+ class MemoryStateUseAfterLoop : public StackObj {
+ public:
+ Node* _use;
+ int _in_idx;
+ int _alias_idx;
+
+ MemoryStateUseAfterLoop(Node* use, int in_idx, int alias_idx) :
+ _use(use), _in_idx(in_idx), _alias_idx(alias_idx) {}
+ MemoryStateUseAfterLoop() : MemoryStateUseAfterLoop(nullptr, 0, 0) {}
+ };
+
+ GrowableArray _memory_state_uses_after_loop;
+
public:
VTransformApplyState(const VLoopAnalyzer& vloop_analyzer, int num_vtnodes) :
_vloop_analyzer(vloop_analyzer),
- _vtnode_idx_to_transformed_node(num_vtnodes, num_vtnodes, nullptr)
+ _vtnode_idx_to_transformed_node(num_vtnodes, num_vtnodes, nullptr),
+ _memory_states(num_slices(), num_slices(), nullptr)
{
+ init_memory_states_and_uses_after_loop();
}
const VLoop& vloop() const { return _vloop_analyzer.vloop(); }
@@ -294,6 +322,25 @@ public:
void set_transformed_node(VTransformNode* vtn, Node* n);
Node* transformed_node(const VTransformNode* vtn) const;
+
+ Node* memory_state(int alias_idx) const { return _memory_states.at(alias_idx); }
+ void set_memory_state(int alias_idx, Node* n) { _memory_states.at_put(alias_idx, n); }
+
+ Node* memory_state(const TypePtr* adr_type) const {
+ int alias_idx = phase()->C->get_alias_index(adr_type);
+ return memory_state(alias_idx);
+ }
+
+ void set_memory_state(const TypePtr* adr_type, Node* n) {
+ int alias_idx = phase()->C->get_alias_index(adr_type);
+ return set_memory_state(alias_idx, n);
+ }
+
+ void fix_memory_state_uses_after_loop();
+
+private:
+ int num_slices() const { return _vloop_analyzer.memory_slices().heads().length(); }
+ void init_memory_states_and_uses_after_loop();
};
// The vtnodes (VTransformNode) resemble the C2 IR Nodes, and model a part of the
@@ -433,6 +480,8 @@ public:
}
virtual VTransformMemopScalarNode* isa_MemopScalar() { return nullptr; }
+ virtual VTransformLoopPhiNode* isa_LoopPhi() { return nullptr; }
+ virtual VTransformCountedLoopNode* isa_CountedLoop() { return nullptr; }
virtual VTransformOuterNode* isa_Outer() { return nullptr; }
virtual VTransformVectorNode* isa_Vector() { return nullptr; }
virtual VTransformElementWiseVectorNode* isa_ElementWiseVector() { return nullptr; }
@@ -448,9 +497,8 @@ public:
virtual const VPointer& vpointer() const { ShouldNotReachHere(); }
virtual VTransformApplyResult apply(VTransformApplyState& apply_state) const = 0;
-
- Node* find_transformed_input(int i, const GrowableArray& vnode_idx_to_transformed_node) const;
-
+ virtual void apply_backedge(VTransformApplyState& apply_state) const {};
+ void apply_vtn_inputs_to_node(Node* n, VTransformApplyState& apply_state) const;
void register_new_node_from_vectorization(VTransformApplyState& apply_state, Node* vn) const;
NOT_PRODUCT(virtual const char* name() const = 0;)
@@ -510,7 +558,9 @@ public:
assert(_node->in(0)->is_Loop(), "phi ctrl must be Loop: %s", _node->in(0)->Name());
}
+ virtual VTransformLoopPhiNode* isa_LoopPhi() override { return this; }
virtual VTransformApplyResult apply(VTransformApplyState& apply_state) const override;
+ virtual void apply_backedge(VTransformApplyState& apply_state) const override;
NOT_PRODUCT(virtual const char* name() const override { return "LoopPhi"; };)
NOT_PRODUCT(virtual void print_spec() const override;)
};
@@ -531,6 +581,16 @@ public:
NOT_PRODUCT(virtual void print_spec() const override;)
};
+// Identity transform for CountedLoop, the only CFG node with a backedge.
+class VTransformCountedLoopNode : public VTransformCFGNode {
+public:
+ VTransformCountedLoopNode(VTransform& vtransform, CountedLoopNode* n) :
+ VTransformCFGNode(vtransform, n) {}
+
+ virtual VTransformCountedLoopNode* isa_CountedLoop() override { return this; }
+ NOT_PRODUCT(virtual const char* name() const override { return "CountedLoop"; };)
+};
+
// Wrapper node for nodes outside the loop that are inputs to nodes in the loop.
// Since we want the loop-internal nodes to be able to reference all inputs as vtnodes,
// we must wrap the inputs that are outside the loop into special vtnodes, too.
@@ -632,22 +692,9 @@ public:
class VTransformVectorNode : public VTransformNode {
private:
const VTransformVectorNodeProperties _properties;
-protected:
- GrowableArray _nodes;
public:
VTransformVectorNode(VTransform& vtransform, const uint req, const VTransformVectorNodeProperties properties) :
- VTransformNode(vtransform, req),
- _properties(properties),
- _nodes(vtransform.arena(),
- properties.vector_length(),
- properties.vector_length(),
- nullptr) {}
-
- void set_nodes(const Node_List* pack) {
- for (uint k = 0; k < pack->size(); k++) {
- _nodes.at_put(k, pack->at(k));
- }
- }
+ VTransformNode(vtransform, req), _properties(properties) {}
virtual VTransformVectorNode* isa_Vector() override { return this; }
void register_new_node_from_vectorization_and_replace_scalar_nodes(VTransformApplyState& apply_state, Node* vn) const;
@@ -749,17 +796,23 @@ public:
_vpointer(vpointer),
_adr_type(adr_type) {}
- const GrowableArray& nodes() const { return _nodes; }
virtual VTransformMemVectorNode* isa_MemVector() override { return this; }
virtual bool is_load_or_store_in_loop() const override { return true; }
virtual const VPointer& vpointer() const override { return _vpointer; }
};
class VTransformLoadVectorNode : public VTransformMemVectorNode {
+private:
+ const LoadNode::ControlDependency _control_dependency;
+
public:
// req = 3 -> [ctrl, mem, adr]
- VTransformLoadVectorNode(VTransform& vtransform, const VTransformVectorNodeProperties properties, const VPointer& vpointer, const TypePtr* adr_type) :
- VTransformMemVectorNode(vtransform, 3, properties, vpointer, adr_type) {}
+ VTransformLoadVectorNode(VTransform& vtransform,
+ const VTransformVectorNodeProperties properties,
+ const VPointer& vpointer,
+ const TypePtr* adr_type,
+ const LoadNode::ControlDependency control_dependency) :
+ VTransformMemVectorNode(vtransform, 3, properties, vpointer, adr_type), _control_dependency(control_dependency) {}
LoadNode::ControlDependency control_dependency() const;
virtual VTransformLoadVectorNode* isa_LoadVector() override { return this; }
virtual bool is_load_in_loop() const override { return true; }
@@ -777,30 +830,4 @@ public:
virtual VTransformApplyResult apply(VTransformApplyState& apply_state) const override;
NOT_PRODUCT(virtual const char* name() const override { return "StoreVector"; };)
};
-
-// Invoke callback on all memops, in the order of the schedule.
-template
-void VTransformGraph::for_each_memop_in_schedule(Callback callback) const {
- assert(_schedule.length() == _vtnodes.length(), "schedule was computed");
-
- for (int i = 0; i < _schedule.length(); i++) {
- VTransformNode* vtn = _schedule.at(i);
-
- // We must ignore nodes outside the loop.
- if (vtn->isa_Outer() != nullptr) { continue; }
-
- VTransformMemopScalarNode* scalar = vtn->isa_MemopScalar();
- if (scalar != nullptr) {
- callback(scalar->node());
- }
-
- VTransformMemVectorNode* vector = vtn->isa_MemVector();
- if (vector != nullptr) {
- for (int j = 0; j < vector->nodes().length(); j++) {
- callback(vector->nodes().at(j)->as_Mem());
- }
- }
- }
-}
-
#endif // SHARE_OPTO_VTRANSFORM_HPP
diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java b/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java
index 8b794e13e3f..06b2afa8a67 100644
--- a/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java
+++ b/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java
@@ -98,9 +98,9 @@ public enum CompilePhase {
PHASEIDEALLOOP2( "PhaseIdealLoop 2"),
PHASEIDEALLOOP3( "PhaseIdealLoop 3"),
AUTO_VECTORIZATION1_BEFORE_APPLY( "AutoVectorization 1, before Apply"),
- AUTO_VECTORIZATION2_AFTER_REORDER( "AutoVectorization 2, after Apply Memop Reordering"),
- AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT( "AutoVectorization 3, after Adjusting Pre-loop Limit"),
- AUTO_VECTORIZATION4_AFTER_SPECULATIVE_RUNTIME_CHECKS("AutoVectorization 4, after Adding Speculative Runtime Checks"),
+ AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT( "AutoVectorization 2, after Adjusting Pre-loop Limit"),
+ AUTO_VECTORIZATION4_AFTER_SPECULATIVE_RUNTIME_CHECKS("AutoVectorization 3, after Adding Speculative Runtime Checks"),
+ AUTO_VECTORIZATION5_AFTER_APPLY( "AutoVectorization 4, after Apply"),
BEFORE_CCP1( "Before PhaseCCP 1"),
CCP1( "PhaseCCP 1"),
ITER_GVN2( "Iter GVN 2"),
From 862119565db311fe0e02e383fd3493601ed23ea8 Mon Sep 17 00:00:00 2001
From: Jan Lahoda
Date: Wed, 8 Oct 2025 05:32:51 +0000
Subject: [PATCH 060/160] 8363917: SwitchBootstraps.enumSwitch() args not
checked as documented
Reviewed-by: liach
---
.../java/lang/runtime/SwitchBootstraps.java | 37 ++++++++------
.../lang/runtime/SwitchBootstrapsTest.java | 49 +++++++++++++++++--
2 files changed, 68 insertions(+), 18 deletions(-)
diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
index f4d82595842..99716baf439 100644
--- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
+++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
@@ -165,22 +165,22 @@ public final class SwitchBootstraps {
* @param lookup Represents a lookup context with the accessibility
* privileges of the caller. When used with {@code invokedynamic},
* this is stacked automatically by the VM.
- * @param invocationName unused
+ * @param invocationName unused, {@code null} is permitted
* @param invocationType The invocation type of the {@code CallSite} with two parameters,
* a reference type, an {@code int}, and {@code int} as a return type.
* @param labels case labels - {@code String} and {@code Integer} constants
* and {@code Class} and {@code EnumDesc} instances, in any combination
* @return a {@code CallSite} returning the first matching element as described above
*
- * @throws NullPointerException if any argument is {@code null}
+ * @throws NullPointerException if any argument is {@code null}, unless noted otherwise
* @throws IllegalArgumentException if any element in the labels array is null
* @throws IllegalArgumentException if the invocation type is not a method type of first parameter of a reference type,
- * second parameter of type {@code int} and with {@code int} as its return type,
+ * second parameter of type {@code int} and with {@code int} as its return type
* @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code String},
* {@code Integer}, {@code Long}, {@code Float}, {@code Double}, {@code Boolean},
- * {@code Class} or {@code EnumDesc}.
+ * {@code Class} or {@code EnumDesc}
* @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code Boolean}
- * when {@code target} is a {@code Boolean.class}.
+ * when {@code target} is a {@code Boolean.class}
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
*/
@@ -255,29 +255,36 @@ public final class SwitchBootstraps {
* enum constant's {@link Enum#name()}.
*
*
- * If no element in the {@code labels} array matches the target, then
- * the method of the call site return the length of the {@code labels} array.
+ * If for a given {@code target} there is no element in the {@code labels}
+ * fulfilling one of the above conditions, then the method of the call
+ * site returns the length of the {@code labels} array.
*
* The value of the {@code restart} index must be between {@code 0} (inclusive) and
* the length of the {@code labels} array (inclusive),
- * both or an {@link IndexOutOfBoundsException} is thrown.
+ * or an {@link IndexOutOfBoundsException} is thrown.
+ *
+ * @apiNote It is permissible for the {@code labels} array to contain {@code String}
+ * values that do not represent any enum constants at runtime.
*
* @param lookup Represents a lookup context with the accessibility
* privileges of the caller. When used with {@code invokedynamic},
* this is stacked automatically by the VM.
- * @param invocationName unused
+ * @param invocationName unused, {@code null} is permitted
* @param invocationType The invocation type of the {@code CallSite} with two parameters,
* an enum type, an {@code int}, and {@code int} as a return type.
* @param labels case labels - {@code String} constants and {@code Class} instances,
* in any combination
* @return a {@code CallSite} returning the first matching element as described above
*
- * @throws NullPointerException if any argument is {@code null}
- * @throws IllegalArgumentException if any element in the labels array is null, if the
- * invocation type is not a method type whose first parameter type is an enum type,
- * second parameter of type {@code int} and whose return type is {@code int},
- * or if {@code labels} contains an element that is not of type {@code String} or
- * {@code Class} of the target enum type.
+ * @throws NullPointerException if any argument is {@code null}, unless noted otherwise
+ * @throws IllegalArgumentException if any element in the labels array is null
+ * @throws IllegalArgumentException if any element in the labels array is an empty {@code String}
+ * @throws IllegalArgumentException if the invocation type is not a method type
+ * whose first parameter type is an enum type,
+ * second parameter of type {@code int} and
+ * whose return type is {@code int}
+ * @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code String} or
+ * {@code Class} equal to the target enum type
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
*/
diff --git a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java
index a231501894f..8c6132b2815 100644
--- a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java
+++ b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2025, Oracle and/or its 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
@@ -155,23 +155,60 @@ public class SwitchBootstrapsTest {
testEnum(E1.B, 0, 0, "B", "C", "A", E1.class);
testEnum(E1.B, 1, 3, "B", "C", "A", E1.class);
try {
- testEnum(E1.B, 1, 3, "B", "C", "A", E2.class);
+ testEnum(E1.B, 0, -1, E2.class);
fail("Didn't get the expected exception.");
} catch (IllegalArgumentException ex) {
//OK
}
try {
- testEnum(E1.B, 1, 3, "B", "C", "A", String.class);
+ testEnum(E1.B, 0, -1, String.class);
fail("Didn't get the expected exception.");
} catch (IllegalArgumentException ex) {
//OK
}
+ try {
+ testEnum(E1.B, 0, -1, 10);
+ fail("Didn't get the expected exception.");
+ } catch (IllegalArgumentException ex) {
+ //OK
+ }
+ try {
+ testEnum(E1.B, 0, -1, new Object());
+ fail("Didn't get the expected exception.");
+ } catch (IllegalArgumentException ex) {
+ //OK
+ }
+ try {
+ testEnum(E1.B, 0, -1, new Object[] { null });
+ fail("Didn't get the expected exception.");
+ } catch (IllegalArgumentException ex) {
+ //OK
+ }
+ try {
+ testEnum(E1.B, 0, -1, "");
+ fail("Didn't get the expected exception.");
+ } catch (IllegalArgumentException ex) {
+ //OK
+ }
+ try {
+ testEnum(E1.B, 0, -1, (Object[]) null);
+ fail("Didn't get the expected exception.");
+ } catch (NullPointerException ex) {
+ //OK
+ }
testEnum(E1.B, 0, 0, "B", "A");
testEnum(E1.A, 0, 1, "B", "A");
testEnum(E1.A, 0, 0, "A", "A", "B");
testEnum(E1.A, 1, 1, "A", "A", "B");
testEnum(E1.A, 2, 3, "A", "A", "B");
testEnum(E1.A, 0, 0);
+ testEnum(E1.B, 0, 2, "A", "OLD_REMOVED_CONSTANT", "B", E1.class);
+ testEnum(E1.B, 1, 2, "A", "OLD_REMOVED_CONSTANT", "B", E1.class);
+
+ //null invocation name:
+ MethodType switchType = MethodType.methodType(int.class, E1.class, int.class);
+ MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), null, switchType)).dynamicInvoker();
+ assertEquals((int) indy.invoke(E1.A, 0), 0);
}
public void testEnumsWithConstants() throws Throwable {
@@ -197,6 +234,9 @@ public class SwitchBootstrapsTest {
testEnum(E.class, E.A, 0, 0, "A", "B", "C");
testEnum(E.class, E.B, 0, 1, "A", "B", "C");
testEnum(E.class, E.C, 0, 2, "A", "B", "C");
+ testEnum(E.class, E.C, 0, 2, "A", "B");
+ testEnum(E.class, E.C, 1, 2, "A", "B");
+ testEnum(E.class, E.C, 2, 2, "A", "B");
}
public void testWrongSwitchTypes() throws Throwable {
@@ -279,6 +319,9 @@ public class SwitchBootstrapsTest {
} catch (IllegalArgumentException ex) {
//OK
}
+ //null invocationName is OK:
+ BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), null, switchType,
+ new Object[] {Object.class});
}
private static AtomicBoolean enumInitialized = new AtomicBoolean();
From bd25db1fb8573fc908f7a8a96bca417b1d44689a Mon Sep 17 00:00:00 2001
From: Matthias Baesken
Date: Wed, 8 Oct 2025 07:02:34 +0000
Subject: [PATCH 061/160] 8368960: Adjust java UL logging in the build
Reviewed-by: erikj, dholmes
---
make/ToolsJdk.gmk | 2 +-
make/autoconf/boot-jdk.m4 | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/make/ToolsJdk.gmk b/make/ToolsJdk.gmk
index ae0dd069c80..629cadbf83a 100644
--- a/make/ToolsJdk.gmk
+++ b/make/ToolsJdk.gmk
@@ -63,7 +63,7 @@ TOOL_GENERATECURRENCYDATA = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_
TOOL_TZDB = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
build.tools.tzdb.TzdbZoneRulesCompiler
-TOOL_BLOCKED_CERTS = $(JAVA_SMALL) -Xlog:disable -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
+TOOL_BLOCKED_CERTS = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
--add-exports java.base/sun.security.util=ALL-UNNAMED \
build.tools.blockedcertsconverter.BlockedCertsConverter
diff --git a/make/autoconf/boot-jdk.m4 b/make/autoconf/boot-jdk.m4
index 1dd768b2ae1..adc9afc349d 100644
--- a/make/autoconf/boot-jdk.m4
+++ b/make/autoconf/boot-jdk.m4
@@ -444,6 +444,9 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS],
# Force en-US environment
UTIL_ADD_JVM_ARG_IF_OK([-Duser.language=en -Duser.country=US],boot_jdk_jvmargs,[$JAVA])
+ UTIL_ADD_JVM_ARG_IF_OK([-Xlog:all=off:stdout],boot_jdk_jvmargs,[$JAVA])
+ UTIL_ADD_JVM_ARG_IF_OK([-Xlog:all=warning:stderr],boot_jdk_jvmargs,[$JAVA])
+
if test "x$BOOTJDK_USE_LOCAL_CDS" = xtrue; then
# Use our own CDS archive
UTIL_ADD_JVM_ARG_IF_OK([$boot_jdk_cds_args -Xshare:auto],boot_jdk_jvmargs,[$JAVA])
From d27649fe22a5bed9db72ac6c2595ac91f1fa28f8 Mon Sep 17 00:00:00 2001
From: Johannes Bechberger
Date: Wed, 8 Oct 2025 08:03:32 +0000
Subject: [PATCH 062/160] 8367302: New test
jdk/jfr/event/profiling/TestCPUTimeSampleQueueAutoSizes.java from JDK-8366082
is failing
Reviewed-by: dholmes, apangin
---
.../sampling/jfrCPUTimeThreadSampler.cpp | 23 +--
.../sampling/jfrCPUTimeThreadSampler.hpp | 7 +-
src/hotspot/share/prims/whitebox.cpp | 16 +--
test/jdk/ProblemList-Xcomp.txt | 1 -
.../TestCPUTimeSampleQueueAutoSizes.java | 131 +++++++++++-------
test/lib/jdk/test/whitebox/WhiteBox.java | 5 +-
6 files changed, 92 insertions(+), 91 deletions(-)
diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp
index 2ce1a93455b..7507b9c994e 100644
--- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp
+++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp
@@ -230,8 +230,7 @@ class JfrCPUSamplerThread : public NonJavaThread {
volatile bool _is_async_processing_of_cpu_time_jfr_requests_triggered;
volatile bool _warned_about_timer_creation_failure;
volatile bool _signal_handler_installed;
- DEBUG_ONLY(volatile bool _out_of_stack_walking_enabled;)
- DEBUG_ONLY(volatile u8 _out_of_stack_walking_iterations;)
+ DEBUG_ONLY(volatile bool _out_of_stack_walking_enabled = true;)
static const u4 STOP_SIGNAL_BIT = 0x80000000;
@@ -283,10 +282,6 @@ public:
void set_out_of_stack_walking_enabled(bool runnable) {
AtomicAccess::release_store(&_out_of_stack_walking_enabled, runnable);
}
-
- u8 out_of_stack_walking_iterations() const {
- return AtomicAccess::load(&_out_of_stack_walking_iterations);
- }
#endif
};
@@ -394,7 +389,6 @@ void JfrCPUSamplerThread::run() {
}
DEBUG_ONLY(if (AtomicAccess::load_acquire(&_out_of_stack_walking_enabled)) {)
if (AtomicAccess::cmpxchg(&_is_async_processing_of_cpu_time_jfr_requests_triggered, true, false)) {
- DEBUG_ONLY(AtomicAccess::inc(&_out_of_stack_walking_iterations);)
stackwalk_threads_in_native();
}
DEBUG_ONLY(})
@@ -588,18 +582,14 @@ void JfrCPUTimeThreadSampling::handle_timer_signal(siginfo_t* info, void* contex
}
#ifdef ASSERT
-void JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(bool runnable) {
+bool JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(bool runnable) {
if (_instance != nullptr && _instance->_sampler != nullptr) {
_instance->_sampler->set_out_of_stack_walking_enabled(runnable);
+ return true;
+ } else {
+ return false;
}
}
-
-u8 JfrCPUTimeThreadSampling::out_of_stack_walking_iterations() {
- if (_instance != nullptr && _instance->_sampler != nullptr) {
- return _instance->_sampler->out_of_stack_walking_iterations();
- }
- return 0;
-}
#endif
void JfrCPUSamplerThread::sample_thread(JfrSampleRequest& request, void* ucontext, JavaThread* jt, JfrThreadLocal* tl, JfrTicks& now) {
@@ -872,8 +862,9 @@ void JfrCPUTimeThreadSampling::on_javathread_terminate(JavaThread* thread) {
}
#ifdef ASSERT
-static void set_out_of_stack_walking_enabled(bool runnable) {
+bool JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(bool runnable) {
warn();
+ return false;
}
#endif
diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp
index e17e63fc3ed..e7c915fc8be 100644
--- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp
+++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp
@@ -139,9 +139,7 @@ class JfrCPUTimeThreadSampling : public JfrCHeapObj {
static void trigger_async_processing_of_cpu_time_jfr_requests();
- DEBUG_ONLY(static void set_out_of_stack_walking_enabled(bool runnable);)
-
- DEBUG_ONLY(static u8 out_of_stack_walking_iterations();)
+ DEBUG_ONLY(static bool set_out_of_stack_walking_enabled(bool runnable);)
};
#else
@@ -162,8 +160,7 @@ private:
static void on_javathread_create(JavaThread* thread);
static void on_javathread_terminate(JavaThread* thread);
- DEBUG_ONLY(static void set_out_of_stack_walking_enabled(bool runnable));
- DEBUG_ONLY(static u8 out_of_stack_walking_iterations();)
+ DEBUG_ONLY(static bool set_out_of_stack_walking_enabled(bool runnable));
};
#endif // defined(LINUX)
diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp
index 1ecd105f218..b4d100341e0 100644
--- a/src/hotspot/share/prims/whitebox.cpp
+++ b/src/hotspot/share/prims/whitebox.cpp
@@ -2710,7 +2710,7 @@ WB_ENTRY(void, WB_WaitUnsafe(JNIEnv* env, jobject wb, jint time))
os::naked_short_sleep(time);
WB_END
-WB_ENTRY(void, WB_BusyWait(JNIEnv* env, jobject wb, jint time))
+WB_ENTRY(void, WB_BusyWaitCPUTime(JNIEnv* env, jobject wb, jint time))
ThreadToNativeFromVM ttn(thread);
u8 start = os::current_thread_cpu_time();
u8 target_duration = time * (u8)1000000;
@@ -2721,21 +2721,12 @@ WB_END
WB_ENTRY(jboolean, WB_CPUSamplerSetOutOfStackWalking(JNIEnv* env, jobject wb, jboolean enable))
#if defined(ASSERT) && INCLUDE_JFR && defined(LINUX)
- JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(enable == JNI_TRUE);
- return JNI_TRUE;
+ return JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(enable == JNI_TRUE) ? JNI_TRUE : JNI_FALSE;
#else
return JNI_FALSE;
#endif
WB_END
-WB_ENTRY(jlong, WB_CPUSamplerOutOfStackWalkingIterations(JNIEnv* env, jobject wb))
- #if defined(ASSERT) && INCLUDE_JFR && defined(LINUX)
- return (jlong)JfrCPUTimeThreadSampling::out_of_stack_walking_iterations();
- #else
- return 0;
- #endif
-WB_END
-
WB_ENTRY(jstring, WB_GetLibcName(JNIEnv* env, jobject o))
ThreadToNativeFromVM ttn(thread);
jstring info_string = env->NewStringUTF(XSTR(LIBC));
@@ -3092,9 +3083,8 @@ static JNINativeMethod methods[] = {
{CC"isJVMTIIncluded", CC"()Z", (void*)&WB_IsJVMTIIncluded},
{CC"waitUnsafe", CC"(I)V", (void*)&WB_WaitUnsafe},
- {CC"busyWait", CC"(I)V", (void*)&WB_BusyWait},
+ {CC"busyWaitCPUTime", CC"(I)V", (void*)&WB_BusyWaitCPUTime},
{CC"cpuSamplerSetOutOfStackWalking", CC"(Z)Z", (void*)&WB_CPUSamplerSetOutOfStackWalking},
- {CC"cpuSamplerOutOfStackWalkingIterations", CC"()J",(void*)&WB_CPUSamplerOutOfStackWalkingIterations},
{CC"getLibcName", CC"()Ljava/lang/String;", (void*)&WB_GetLibcName},
{CC"pinObject", CC"(Ljava/lang/Object;)V", (void*)&WB_PinObject},
diff --git a/test/jdk/ProblemList-Xcomp.txt b/test/jdk/ProblemList-Xcomp.txt
index 8e37e6e15d0..5ed171a1fea 100644
--- a/test/jdk/ProblemList-Xcomp.txt
+++ b/test/jdk/ProblemList-Xcomp.txt
@@ -29,4 +29,3 @@
java/lang/invoke/MethodHandles/CatchExceptionTest.java 8146623 generic-all
java/lang/reflect/callerCache/ReflectionCallerCacheTest.java 8332028 generic-all
-jdk/jfr/event/profiling/TestCPUTimeSampleQueueAutoSizes.java 8367302 linux-all
diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleQueueAutoSizes.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleQueueAutoSizes.java
index 1ea96e3bad3..819329aabf5 100644
--- a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleQueueAutoSizes.java
+++ b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleQueueAutoSizes.java
@@ -24,6 +24,7 @@
package jdk.jfr.event.profiling;
import java.time.Duration;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.stream.Collectors;
@@ -41,8 +42,15 @@ import jdk.test.whitebox.WhiteBox;
/*
* Tests the sample queues increase in size as needed, when loss is recorded.
+ *
+ * The test starts CPU time sampling with a short interval (1ms), disabling
+ * out-of-stack sample processing for the duration of the test.
+ * It now runs in native for one second, to cause queue overflows,
+ * then it comes back into Java to trigger the queue walking.
+ * Repeats the cycle 5 times and verifies that the loss decreases from the first
+ * to the last iteration.
* @test
- * @requires vm.hasJFR & os.family == "linux" & vm.debug
+ * @requires vm.hasJFR & os.family == "linux" & vm.debug & vm.flagless
* @library /test/lib
* @modules jdk.jfr/jdk.jfr.internal
* @build jdk.test.whitebox.WhiteBox
@@ -54,15 +62,13 @@ public class TestCPUTimeSampleQueueAutoSizes {
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
- private static final String BURST_THREAD_NAME = "Burst-Thread-1";
-
- static volatile boolean alive = true;
-
record LossEvent(long relativeTimeMillis, long lostSamples) {}
/** A data collection from the CPUTimeSampleLost events for the burst thread */
static class LossEventCollection {
private final List events = new ArrayList<>();
+ private final List sampleEventsInTimeBox = new ArrayList<>();
+ private final List timeBoxEnds = new ArrayList<>();
public synchronized void addEvent(LossEvent event) {
events.add(event);
@@ -74,81 +80,100 @@ public class TestCPUTimeSampleQueueAutoSizes {
.collect(Collectors.toList());
}
- public List getEventsPerInterval(long widthMillis, long stopTimeMillis) {
+ public synchronized List getEventsPerTimeBox() {
List ret = new ArrayList<>();
- for (long start = 0; start < stopTimeMillis; start += widthMillis) {
- long actualStart = Math.min(start, stopTimeMillis - widthMillis);
+ AtomicLong previousEnd = new AtomicLong(0);
+ for (Long timeBoxEnd : timeBoxEnds) {
long lostSamples = events.stream()
- .filter(e -> e.relativeTimeMillis >= actualStart && e.relativeTimeMillis < actualStart + widthMillis)
+ .filter(e -> e.relativeTimeMillis >= previousEnd.get() && e.relativeTimeMillis <= timeBoxEnd)
.mapToLong(e -> e.lostSamples)
.sum();
- ret.add(new LossEvent(actualStart, lostSamples));
+ ret.add(new LossEvent(previousEnd.get(), lostSamples));
+ previousEnd.set(timeBoxEnd);
}
return ret;
}
+ public synchronized void addTimeBoxEnd(long timeBoxEnd, long sampleEvents) {
+ timeBoxEnds.add(timeBoxEnd);
+ sampleEventsInTimeBox.add(sampleEvents);
+ }
+
+ public synchronized void print() {
+ System.out.println("Loss event information:");
+ for (int i = 0; i < timeBoxEnds.size(); i++) {
+ System.out.println(" Time box end: " + timeBoxEnds.get(i) + ", sample events: " + sampleEventsInTimeBox.get(i));
+ }
+ for (LossEvent e : events) {
+ System.out.println(" Lost samples event: " + e.lostSamples + " at " + e.relativeTimeMillis);
+ }
+ for (LossEvent e : getEventsPerTimeBox()) {
+ System.out.println(" Lost samples in time box ending at " + e.relativeTimeMillis + ": " + e.lostSamples);
+ }
+ }
}
public static void main(String[] args) throws Exception {
try (RecordingStream rs = new RecordingStream()) {
// setup recording
- AtomicLong firstSampleTimeMillis = new AtomicLong(0);
- AtomicLong lastSampleTimeMillis = new AtomicLong(0);
+ long burstThreadId = Thread.currentThread().threadId();
+ final long startTimeMillis = Instant.now().toEpochMilli();
LossEventCollection lossEvents = new LossEventCollection();
+ AtomicLong sampleEventCountInTimeBox = new AtomicLong(0);
rs.enable(EventNames.CPUTimeSample).with("throttle", "1ms");
- rs.onEvent(EventNames.CPUTimeSample, e -> {
- if (firstSampleTimeMillis.get() == 0 && e.getThread("eventThread").getJavaName().equals(BURST_THREAD_NAME)) {
- firstSampleTimeMillis.set(e.getStartTime().toEpochMilli());
- }
- if (e.getThread("eventThread").getJavaName().equals(BURST_THREAD_NAME)) {
- lastSampleTimeMillis.set(e.getStartTime().toEpochMilli());
- }
- });
rs.enable(EventNames.CPUTimeSamplesLost);
rs.onEvent(EventNames.CPUTimeSamplesLost, e -> {
- if (e.getThread("eventThread").getJavaName().equals(BURST_THREAD_NAME)) {
+ if (e.getThread("eventThread").getJavaThreadId() == burstThreadId) {
long eventTime = e.getStartTime().toEpochMilli();
- long relativeTime = firstSampleTimeMillis.get() > 0 ? (eventTime - firstSampleTimeMillis.get()) : eventTime;
- System.out.println("Lost samples: " + e.getLong("lostSamples") + " at " + relativeTime);
+ long relativeTime = eventTime - startTimeMillis;
+ System.out.println("Lost samples: " + e.getLong("lostSamples") + " at " + relativeTime + " start time " + startTimeMillis);
lossEvents.addEvent(new LossEvent(relativeTime, e.getLong("lostSamples")));
}
});
- WHITE_BOX.cpuSamplerSetOutOfStackWalking(false);
+ rs.onEvent(EventNames.CPUTimeSample, e -> {
+ if (e.getThread("eventThread").getJavaThreadId() == burstThreadId) {
+ sampleEventCountInTimeBox.incrementAndGet();
+ }
+ });
rs.startAsync();
- // this thread runs all along
- Thread burstThread = new Thread(() -> WHITE_BOX.busyWait(11000));
- burstThread.setName(BURST_THREAD_NAME);
- burstThread.start();
- // now we toggle out-of-stack-walking off, wait 1 second and then turn it on for 500ms a few times
+ // we disable the out-of-stack walking so that the queue fills up and overflows
+ // while we are in native code
+ disableOutOfStackWalking();
+
+
for (int i = 0; i < 5; i++) {
- boolean supported = WHITE_BOX.cpuSamplerSetOutOfStackWalking(false);
- if (!supported) {
- System.out.println("Out-of-stack-walking not supported, skipping test");
- Asserts.assertFalse(true);
- return;
- }
- Thread.sleep(700);
- long iterations = WHITE_BOX.cpuSamplerOutOfStackWalkingIterations();
- WHITE_BOX.cpuSamplerSetOutOfStackWalking(true);
- Thread.sleep(300);
- while (WHITE_BOX.cpuSamplerOutOfStackWalkingIterations() == iterations) {
- Thread.sleep(50); // just to make sure the stack walking really ran
- }
+ // run in native for one second
+ WHITE_BOX.busyWaitCPUTime(1000);
+ // going out-of-native at the end of the previous call should have triggered
+ // the safepoint handler, thereby also triggering the stack walking and creation
+ // of the loss event
+ WHITE_BOX.forceSafepoint(); // just to be sure
+ lossEvents.addTimeBoxEnd(Instant.now().toEpochMilli() - startTimeMillis, sampleEventCountInTimeBox.get());
+ sampleEventCountInTimeBox.set(0);
}
+
+ rs.stop();
rs.close();
- checkThatLossDecreased(lossEvents, lastSampleTimeMillis.get() - firstSampleTimeMillis.get());
+
+ enableOutOfStackWalking();
+
+ checkThatLossDecreased(lossEvents);
}
}
- static void checkThatLossDecreased(LossEventCollection lossEvents, long lastSampleTimeMillis) {
- List intervalLosses = lossEvents.getEventsPerInterval(1000, lastSampleTimeMillis);
- for (LossEvent interval : intervalLosses) {
- System.out.println("Lost samples in interval " + interval.relativeTimeMillis + ": " + interval.lostSamples);
- }
- // check that there are at least 3 intervals
- Asserts.assertTrue(intervalLosses.size() > 2);
- // check that the second to last interval has far fewer lost samples than the first
- Asserts.assertTrue(intervalLosses.get(intervalLosses.size() - 2).lostSamples <
- intervalLosses.get(0).lostSamples / 2);
+ static void disableOutOfStackWalking() {
+ Asserts.assertTrue(WHITE_BOX.cpuSamplerSetOutOfStackWalking(false), "Out-of-stack-walking not supported");
+ }
+
+ static void enableOutOfStackWalking() {
+ WHITE_BOX.cpuSamplerSetOutOfStackWalking(true);
+ }
+
+ static void checkThatLossDecreased(LossEventCollection lossEvents) {
+ lossEvents.print();
+ List timeBoxedLosses = lossEvents.getEventsPerTimeBox();
+ // check that the last time box has far fewer lost samples than the first
+ Asserts.assertTrue(timeBoxedLosses.get(timeBoxedLosses.size() - 1).lostSamples <=
+ timeBoxedLosses.get(0).lostSamples / 2);
}
}
diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java
index 669ec48b619..d07dedd2a8c 100644
--- a/test/lib/jdk/test/whitebox/WhiteBox.java
+++ b/test/lib/jdk/test/whitebox/WhiteBox.java
@@ -847,13 +847,12 @@ public class WhiteBox {
public native void waitUnsafe(int time_ms);
- public native void busyWait(int cpuTimeMs);
+ public native void busyWaitCPUTime(int cpuTimeMs);
+
// returns true if supported, false if not
public native boolean cpuSamplerSetOutOfStackWalking(boolean enable);
- public native long cpuSamplerOutOfStackWalkingIterations();
-
public native void pinObject(Object o);
public native void unpinObject(Object o);
From f58e17fd27e868e4a8816befc4c4bb8946c1f7fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ant=C3=B3n=20Seoane=20Ampudia?=
Date: Wed, 8 Oct 2025 08:58:58 +0000
Subject: [PATCH 063/160] 8368780: IGV: Upgrade to Netbeans Platform 27
Reviewed-by: rcastanedalo, chagedorn
---
src/utils/IdealGraphVisualizer/Filter/pom.xml | 4 ++--
src/utils/IdealGraphVisualizer/README.md | 2 +-
src/utils/IdealGraphVisualizer/pom.xml | 12 ++++++++----
3 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/src/utils/IdealGraphVisualizer/Filter/pom.xml b/src/utils/IdealGraphVisualizer/Filter/pom.xml
index 176f7a80180..c22ce274493 100644
--- a/src/utils/IdealGraphVisualizer/Filter/pom.xml
+++ b/src/utils/IdealGraphVisualizer/Filter/pom.xml
@@ -1,6 +1,6 @@