1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package optkl.codebuilders;
26
27 import jdk.incubator.code.Block;
28 import jdk.incubator.code.Op;
29 import jdk.incubator.code.CodeType;
30 import jdk.incubator.code.dialect.core.CoreOp;
31 import jdk.incubator.code.dialect.core.VarType;
32 import jdk.incubator.code.dialect.java.ArrayType;
33 import jdk.incubator.code.dialect.java.JavaOp;
34 import jdk.incubator.code.dialect.java.JavaType;
35 import optkl.FuncOpParams;
36 import optkl.OpHelper;
37 import optkl.ParamVar;
38 import optkl.util.Mutable;
39 import optkl.util.ops.Precedence;
40
41 import java.util.function.Consumer;
42
43 import static optkl.OpHelper.FieldAccess.fieldAccess;
44 import static optkl.OpHelper.Invoke.invoke;
45 import static optkl.OpHelper.Ternary.ternary;
46
47 public abstract class JavaOrC99StyleCodeBuilder<T extends JavaOrC99StyleCodeBuilder<T,SCBC>,SCBC extends ScopedCodeBuilderContext>
48 extends ScopedCodeBuilder<T,SCBC>
49 implements BabylonOpDispatcher<T,SCBC>{
50
51 ScopedCodeBuilderContext scopedCodeBuilderContext(){
52 throw new RuntimeException("we are fake. This should be removed ");
53 }
54
55 public T body(Consumer<T> consumer) {
56 return braceNlIndented(consumer);
57 }
58
59 public T tern(Consumer<T> test, Consumer<T> ifTrue, Consumer<T> ifFalse){
60 return paren(test).questionMark().paren(ifTrue).colon().paren(ifFalse);
61 }
62
63 public T stmnt(Consumer<T> consumer){
64 consumer.accept(self());
65 return semicolon();
66 }
67
68 public T func(
69 Consumer<T> type,
70 String funcName,
71 Consumer<T> args,
72 Consumer<T> body) {
73 type.accept(self());
74 return sp().id(funcName).paren(args).body(body).nl().nl();
75 }
76
77 public final T assign(Consumer<T> lhs, Consumer<T> rhs){
78 lhs.accept(self());
79 sp().equals().sp();
80 rhs.accept(self());
81 return self();
82 }
83
84 public final T cast(Consumer<T> type){
85 return paren(_-> type.accept(self()));
86 }
87
88 public final T returnKeyword(Consumer<T> exp){
89 // return returnKeyword().space().paren(_-> exp.accept(self())).semicolon(); // This looks wrong. it is very rare for us to have to add trailing semicolons
90 returnKeyword().sp();
91 exp.accept(self());
92 return semicolon();
93 }
94 public final T forLoop(Consumer<T> init, Consumer<T> test, Consumer<T>mutate, Consumer<T>body) {
95 return forKeyword()
96 .paren(_->{
97 init.accept(self());
98 semicolon().sp();
99 test.accept(self());
100 semicolon().sp();mutate.accept(self());
101 })
102 .braceNlIndented(body::accept);
103 }
104
105 public final T literal(CodeType codeType, String string) {
106 if (codeType.toString().equals("java.lang.String")) {
107 dquote().escaped(string).dquote();
108 } else if (codeType.equals(JavaType.FLOAT)) {
109 literal(string).literal("f");
110 } else {
111 literal(string);
112 }
113 return self();
114 }
115
116
117 @Override
118 public T type(JavaType javaType) {
119 blockInlineComment("javaType: " + javaType);
120 return type(javaType.toString());
121 }
122
123 public T type(CodeType codeType) {
124 return type(codeType.toString());
125 }
126
127 @Override
128 public T varLoadOp( CoreOp.VarAccessOp.VarLoadOp varLoadOp) {
129 Op resolve = scopedCodeBuilderContext().resolve(varLoadOp.operands().getFirst());
130 if (resolve instanceof CoreOp.VarOp varOp) {
131 varName(varOp);
132 }else if (resolve instanceof CoreOp.VarAccessOp.VarLoadOp){
133 varName(varLoadOp);
134 }
135 return self();
136 }
137
138 @Override
139 public T varStoreOp( CoreOp.VarAccessOp.VarStoreOp varStoreOp) {
140 Op op = scopedCodeBuilderContext().resolve(varStoreOp.operands().getFirst());
141 varName((CoreOp.VarOp) op);
142 equals().parenthesisIfNeeded( varStoreOp, ((Op.Result)varStoreOp.operands().get(1)).op());
143 return self();
144 }
145
146 @Override
147 public T varOp(CoreOp.VarOp varOp) {
148 if (varOp.isUninitialized()) {
149 type((JavaType) varOp.varValueType()).sp().varName(varOp);
150 } else {
151 if (scopedCodeBuilderContext().isVarOpFinal(varOp)) {
152 constKeyword().sp();
153 }
154
155 var first = varOp.operands().getFirst();
156 if (first instanceof Op.Result result) {
157 parenthesisIfNeeded(varOp, result.op());
158 } else if (first instanceof Block.Parameter parameter) {
159 var p1 = parameter.declaringBlock().parameters().getFirst();
160
161 var r = parameter.uses().iterator().next();
162 //parenthesisIfNeeded( varOp, r.op());
163 // if (r.op() instanceof CoreOp.VarOp varOp1){
164 // identifier(varOp1.varName());
165 // }
166 blockInlineComment("param " + r);
167 } else {
168 blockInlineComment("look at varOp " + first);
169 }
170 }
171 return self();
172 }
173
174 @Override
175 public final T varOp( CoreOp.VarOp varOp, ParamVar paramVar) {
176 varName(varOp);
177 return self();
178 }
179
180 @Override
181 public T fieldLoadOp( JavaOp.FieldAccessOp.FieldLoadOp fieldLoadOp) {
182 var fieldAccess = fieldAccess(scopedCodeBuilderContext().lookup(),fieldLoadOp);
183 if (fieldAccess.operandCount()==0 && fieldAccess.isPrimitive() ) {
184 literal(fieldAccess.getStaticFinalPrimitiveValue().toString());
185 } else {
186 id(fieldAccess.name());
187 }
188 return self();
189 }
190
191 @Override
192 public final T fieldStoreOp( JavaOp.FieldAccessOp.FieldStoreOp fieldStoreOp) {
193 var fieldAccess = fieldAccess(scopedCodeBuilderContext().lookup(),fieldStoreOp);
194 id(fieldAccess.name()).sp().equals().sp();
195 recurse(((Op.Result)fieldAccess.op().operands().get(0)).op());
196 dot();
197 recurse(((Op.Result)fieldAccess.op().operands().get(1)).op());
198 return self();
199 }
200
201
202 @Override
203 public final T unaryOp( JavaOp.UnaryOp unaryOp) {
204 symbol(unaryOp).parenthesisIfNeeded( unaryOp, ((Op.Result)unaryOp.operands().getFirst()).op());
205 return self();
206 }
207
208 @Override
209 public final T binaryOp( JavaOp.BinaryOp binaryOp) {
210 parenthesisIfNeeded( binaryOp, OpHelper.lhsResult(binaryOp).op());
211 symbol(binaryOp);
212 parenthesisIfNeeded( binaryOp, OpHelper.rhsResult(binaryOp).op());
213 return self();
214 }
215
216
217 @Override
218 public final T conditionalOp( JavaOp.ConditionalOp logicalOp) {
219 OpHelper.lhsOps(logicalOp).stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
220 sp().symbol(logicalOp).sp();
221 OpHelper.rhsOps(logicalOp).stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o-> recurse( o));
222 return self();
223 }
224
225 @Override
226 public final T compareOp( JavaOp.CompareOp compareOp) {
227 parenthesisIfNeeded( compareOp, OpHelper.lhsResult(compareOp).op());
228 symbol(compareOp);
229 parenthesisIfNeeded( compareOp, OpHelper.rhsResult(compareOp).op());
230 return self();
231 }
232
233 @Override
234 public T convOp( JavaOp.ConvOp convOp) {
235 // TODO: I think we need to work out how to handle doubles. If I remove this OpenCL on MAC complains (no FP64)
236 if (convOp.resultType() == JavaType.DOUBLE) {
237 paren(_ -> type(JavaType.FLOAT)); // why double to float?
238 } else {
239 paren(_ -> type((JavaType)convOp.resultType()));
240 }
241 parenthesisIfNeeded( convOp, ((Op.Result) convOp.operands().getFirst()).op());
242 return self();
243 }
244
245 @Override
246 public final T constantOp( CoreOp.ConstantOp constantOp) {
247 if (constantOp.value() == null) {
248 nullConst();
249 } else {
250 literal(constantOp.resultType(),constantOp.value().toString());
251 }
252 return self();
253 }
254
255 @Override
256 public final T yieldOp( CoreOp.YieldOp yieldOp) {
257 if (yieldOp.operands().getFirst() instanceof Op.Result result) {
258 recurse( result.op());
259 }
260 return self();
261 }
262
263 @Override
264 public final T tupleOp( CoreOp.TupleOp tupleOp) {
265 commaSpaceSeparated(tupleOp.operands(),operand->{
266 if (operand instanceof Op.Result result) {
267 recurse( result.op());
268 } else {
269 throw new IllegalStateException("handle tuple");
270 //comment("/* nothing to tuple */");
271 }
272 });
273 return self();
274 }
275
276 @Override
277 public final T funcCallOp( CoreOp.FuncCallOp funcCallOp) {
278 funcName(funcCallOp);
279 paren(_ ->
280 commaSpaceSeparated(
281 funcCallOp.operands().stream().filter(e->e instanceof Op.Result ).map(e->(Op.Result)e),
282 result -> recurse(result.op())
283 )
284 );
285 return self();
286 }
287
288 @Override
289 public final T labeledOp( JavaOp.LabeledOp labeledOp) {
290 var labelNameOp = labeledOp.bodies().getFirst().entryBlock().ops().getFirst();
291 CoreOp.ConstantOp constantOp = (CoreOp.ConstantOp) labelNameOp;
292 literal(constantOp.value().toString()).colon().nl();
293 var forLoopOp = labeledOp.bodies().getFirst().entryBlock().ops().get(1);
294 recurse(forLoopOp);
295 return self();
296 }
297
298 @Override
299 public final T breakOp( JavaOp.BreakOp breakOp) {
300 breakKeyword();
301 if (!breakOp.operands().isEmpty() && breakOp.operands().getFirst() instanceof Op.Result result) {
302 sp();
303 if (result.op() instanceof CoreOp.ConstantOp c) {
304 literal(c.value().toString());
305 }
306 }
307 return self();
308 }
309
310 @Override
311 public final T continueOp( JavaOp.ContinueOp continueOp) {
312 if (!continueOp.operands().isEmpty()
313 && continueOp.operands().getFirst() instanceof Op.Result result
314 && result.op() instanceof CoreOp.ConstantOp c
315 ) {
316 continueKeyword().sp().literal(c.value().toString());
317 } else if (scopedCodeBuilderContext().isInFor()) {
318 // nope
319 } else {
320 continueKeyword();
321 }
322
323 return self();
324 }
325
326 @Override
327 public final T ifOp( JavaOp.IfOp ifOp) {
328 scopedCodeBuilderContext().ifScope(ifOp, () -> {
329 var lastWasBody = Mutable.of(false);
330 var i = Mutable.of(0);
331 // We probably should just use a regular for loop here ;)
332 ifOp.bodies().forEach(b->{
333 int idx = i.get();
334 if (b.yieldType() instanceof JavaType javaType && javaType == JavaType.VOID) {
335 if (ifOp.bodies().size() > idx && ifOp.bodies().get(idx).entryBlock().ops().size() > 1){
336 if (lastWasBody.get()) {
337 elseKeyword();
338 }
339 braceNlIndented(_ ->
340 nlSeparated(OpHelper.Statement.statements(ifOp.bodies().get(idx).entryBlock()),
341 root-> statement(root)
342 ));
343 }
344 lastWasBody.set(true);
345 } else {
346 when(idx>0,_-> elseKeyword().sp());
347 ifKeyword().paren(_ ->
348 ifOp.bodies().get(idx).entryBlock() // get the entryblock if bodies[c.value]
349 .ops().stream().filter(o->o instanceof CoreOp.YieldOp) // we want all the yields
350 .forEach((yield) -> recurse( yield))
351 );
352 lastWasBody.set(false);
353 }
354 i.set(i.get()+1);
355 });
356 });
357 return self();
358 }
359
360 @Override
361 public final T whileOp( JavaOp.WhileOp whileOp) {
362 whileKeyword().paren(_ ->
363 OpHelper.entryBlockOfBodyN(whileOp, 0)
364 .ops().stream().filter(o -> o instanceof CoreOp.YieldOp)
365 .forEach(o -> recurse( o))
366 );
367 braceNlIndented(_ ->
368 nlSeparated(OpHelper.Statement.bodyStatements(whileOp.loopBody()),
369 statement->statement(statement)
370 )
371 );
372 return self();
373 }
374
375 @Override
376 public final T forOp( JavaOp.ForOp forOp) {
377 scopedCodeBuilderContext().forScope(forOp, () ->
378 forKeyword().paren(_ -> {
379 forOp.initBody().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
380 semicolon().sp();
381 forOp.condBody().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
382 semicolon().sp();
383 commaSpaceSeparated(
384 OpHelper.Statement.statements(forOp.updateBody().entryBlock()),
385 op -> recurse( op)
386 );
387 }).braceNlIndented(_ ->
388 nlSeparated(OpHelper.Statement.bodyStatements(forOp.loopBody()),
389 statement ->statement(statement)
390 )
391 )
392 );
393 return self();
394 }
395
396 @Override
397 public T invokeOp( JavaOp.InvokeOp invokeOp) {
398 var invoke = invoke(scopedCodeBuilderContext().lookup(),invokeOp);
399
400 funcName(invoke.op()).paren(_ ->
401 commaSpaceSeparated(invoke.op().operands(),
402 op -> {if (op instanceof Op.Result result) {recurse( result.op());}
403 })
404 );
405
406 return self();
407 }
408
409 @Override
410 public final T conditionalExpressionOp( JavaOp.ConditionalExpressionOp ternaryOp) {
411 OpHelper.Ternary ternary = ternary(scopedCodeBuilderContext().lookup(),ternaryOp);
412 ternary.condBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
413 questionMark();
414 ternary.thenBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
415 colon();
416 ternary.elseBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
417 return self();
418 }
419
420 /**
421 * Wrap paren() of precedence of op is higher than parent.
422 *
423
424 * @param parent
425 * @param child
426 */
427 @Override
428 public final T parenthesisIfNeeded( Op parent, Op child) {
429 return parenWhen(Precedence.needsParenthesis(parent,child), _ -> recurse( child));
430 }
431
432 @Override
433 public final T returnOp( CoreOp.ReturnOp returnOp) {
434 returnKeyword().when(!returnOp.operands().isEmpty(),
435 $-> $.sp().parenthesisIfNeeded( returnOp, ((Op.Result) returnOp.operands().getFirst()).op())
436 );
437 return self();
438 }
439
440 public final T statement(Op op) {
441 recurse( op);
442 if (switch (op){
443 case JavaOp.ForOp _ -> false;
444 case JavaOp.WhileOp _ -> false;
445 case JavaOp.IfOp _ -> false;
446 case JavaOp.LabeledOp _ -> false;
447 case JavaOp.YieldOp _ -> false;
448 case CoreOp.TupleOp _ ->false;
449 default -> true;
450 }
451 ){
452 semicolon();
453 }
454 return self();
455 }
456
457 public T declareParam(FuncOpParams.Info param) {
458 return type((JavaType) param.parameter.type()).sp().varName(param.varOp);
459 }
460
461 @Override
462 public T newOp( JavaOp.NewOp newOp) {
463 newKeyword().sp().type((JavaType) newOp.resultType());
464 if (newOp.operands().isEmpty()){
465 ocparen();
466 }else {
467 if (newOp.resultType() instanceof ArrayType){
468 brace(_ -> {
469 commaSpaceSeparated(newOp.operands(),
470 op -> {
471 if (op instanceof Op.Result result) {
472 recurse( result.op());
473 }
474 });
475 });
476 }else {
477 paren(_ -> {
478 commaSpaceSeparated(newOp.operands(),
479 op -> {
480 if (op instanceof Op.Result result) {
481 recurse( result.op());
482 }
483 });
484 });
485 }
486 }
487 return self();
488 }
489 @Override
490 public T arrayLoadOp( JavaOp.ArrayAccessOp.ArrayLoadOp arrayLoadOp){
491 recurse(((Op.Result)arrayLoadOp.operands().get(0)).op());
492 sbrace(_-> recurse(((Op.Result)arrayLoadOp.operands().get(1)).op()));
493 return self();
494 }
495
496 @Override
497 public T arrayStoreOp( JavaOp.ArrayAccessOp.ArrayStoreOp arrayStoreOp){
498 recurse(((Op.Result)arrayStoreOp.operands().get(0)).op());
499 sbrace(_-> recurse(((Op.Result)arrayStoreOp.operands().get(1)).op()));
500 sp().equals().sp();
501 recurse(((Op.Result)arrayStoreOp.operands().get(2)).op());
502 return self();
503 }
504
505 @Override
506 public T enhancedForOp(JavaOp.EnhancedForOp enhancedForOp){
507 scopedCodeBuilderContext().enhancedForScope(enhancedForOp, () -> {
508 forKeyword().paren(_ -> {
509 // enhancedForOp.initBody().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
510 enhancedForOp.initBody().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> {
511 CoreOp.YieldOp yieldOp = (CoreOp.YieldOp) o;
512 CodeType type = yieldOp.yieldValue().type();
513 if (type instanceof VarType varType) {
514 CodeType codeType1 = varType.valueType();
515 type(codeType1).sp();
516 }
517 Block.Parameter param = enhancedForOp.initBody().entryBlock().parameters().getFirst();
518 if (param.declaringBlock().firstOp() instanceof CoreOp.VarOp varOp) {
519 varName(varOp);
520 }
521 });
522 sp().colon().sp();
523 enhancedForOp.exprBody().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse(o));
524 }).braceNlIndented(_ ->
525 nlSeparated(OpHelper.Statement.bodyStatements(enhancedForOp.loopBody()), this::statement));
526 });
527 return self();
528 }
529
530 @Override
531 public T blockOp( JavaOp.BlockOp blockOp) {
532 return braceNlIndented(_-> nlSeparated(OpHelper.Statement.statements(blockOp.body().entryBlock()), this::statement));
533 }
534
535 @Override
536 public T concatOp( JavaOp.ConcatOp concatOp) {
537 return
538 recurse( ((Op.Result)concatOp.operands().get(0)).op()).
539 add().recurse( ((Op.Result)concatOp.operands().get(1)).op());
540 }
541
542 @Override
543 public final T lambdaOp( JavaOp.LambdaOp lambdaOp) {
544 scopedCodeBuilderContext().lambdaScope(lambdaOp,()-> {
545 var parameters = lambdaOp.body().entryBlock().parameters();
546 // if (parameters.isEmpty()) {
547 // ocparen();
548 //}else{
549 parenWhen(parameters.size()!=1,_->{
550 commaSpaceSeparated(parameters,$->
551 varName((CoreOp.VarOp)$.uses().stream().findFirst().get().op())
552 );
553 });
554 //}
555 sp().rarrow().sp();
556 braceNlIndented(_ -> {
557 nlSeparated(OpHelper.Statement.bodyStatements(lambdaOp.body()),
558 op->{
559 if (op instanceof CoreOp.VarOp varOp
560 && varOp.operands().getFirst() instanceof Block.Parameter parameter
561 ){
562 varName(varOp);
563 }else {
564 statement(op);
565 }
566 }
567 );
568 });
569 }
570 );
571 return self();
572 }
573 }