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 
 26 package hat;
 27 
 28 import java.util.Arrays;
 29 import java.util.List;
 30 import java.util.Optional;
 31 
 32 /**
 33  * Config bits shared across Java and Native code.
 34  *
 35  * <p>
 36  * After modifying this file, run the following command to sync the corresponding
 37  * config with the native side.
 38  * </p>
 39  *
 40  * <p>
 41  * <code>
 42  *     java @.ffisync
 43  * </code>
 44  * </p>
 45  */
 46 public class Config {
 47 
 48     public record Bit(int index, int size, String name, String alt, String description) implements Comparable<Bit> {
 49         static Bit of(int index, int size, String name, String alt, String description){
 50             return new Bit(index,size,name, alt, description);
 51         }
 52         public static Bit of(int index, String name, String alt,  String description){
 53             return new Bit(index,1,name, alt,description);
 54         }
 55 
 56         public static Bit nextBit(Bit bit, int size, String name, String alt,String description){
 57             return new Bit(bit.index+bit.size,size,name, alt, description);
 58         }
 59         public static Bit nextBit(Bit bit, String name, String alt, String description){
 60             return nextBit(bit, 1,name,alt, description);
 61         }
 62         @Override
 63         public int compareTo(Bit bit) {
 64             return Integer.compare(index, bit.index);
 65         }
 66 
 67         public boolean isBitSet(int bits) {
 68             return (mask() & bits) == mask();
 69         }
 70 
 71         public boolean isSet(Config config) {
 72             return (mask() & config.bits) == mask();
 73         }
 74 
 75         public int mask() {
 76             return ((1 << size) - 1) << index;
 77         }
 78 
 79         public String maskString() {
 80             return Integer.toBinaryString(mask());
 81         }
 82     }
 83 
 84     public static final Bit PLATFORM =  Bit.of(0,    3, "PLATFORM","P", "FFI ONLY platform id (0-7)");
 85     public static final Bit DEVICE = Bit.nextBit(PLATFORM, 3, "DEVICE","D","FFI ONLY device id (0-7)");
 86 
 87     private static final Bit MINIMIZE_COPIES =  Bit.nextBit(DEVICE, "MINIMIZE_COPIES","MC","FFI ONLY Try to minimize copies");
 88     public boolean minimizeCopies() {
 89         return MINIMIZE_COPIES.isSet(this);
 90     }
 91 
 92     private static final Bit TRACE = Bit.nextBit(MINIMIZE_COPIES,"TRACE", "T","FFI ONLY trace code");
 93 
 94     private static final Bit PROFILE = Bit.nextBit(TRACE, "PROFILE", "P","FFI ONLY Turn on profiling");
 95 
 96     private static final Bit SHOW_CODE = Bit.nextBit(PROFILE,"SHOW_CODE","SC","Show generated code (PTX/OpenCL/CUDA)");
 97     public boolean showCode() {
 98         return SHOW_CODE.isSet(this);
 99     }
100 
101     private static final Bit SHOW_KERNEL_MODEL = Bit.nextBit(SHOW_CODE,"SHOW_KERNEL_MODEL", "SKM","Show (via OpWriter) Kernel Model");
102     public boolean showKernelModel() {
103         return SHOW_KERNEL_MODEL.isSet(this);
104     }
105 
106     private static final Bit SHOW_COMPUTE_MODEL = Bit.nextBit(SHOW_KERNEL_MODEL,"SHOW_COMPUTE_MODEL", "SCM","Show (via OpWriter) Compute Model");
107     public boolean showComputeModel() {
108         return SHOW_COMPUTE_MODEL.isSet(this);
109     }
110 
111     private static final Bit SHOW_DEVICE_INFO = Bit.nextBit(SHOW_COMPUTE_MODEL, "SHOW_DEVICE_INFO", "SDI","FFI show platform and device info");
112 
113     public static final Bit INFO = Bit.nextBit(SHOW_DEVICE_INFO, "INFO", "I","INFO level logging");
114 
115     public static final Bit WARN = Bit.nextBit(INFO, "WARN", "W","WARN(ing) level logging ");
116 
117     public static final Bit UNIT = Bit.nextBit(WARN, "UNIT", "U","UNIT test level logging  ");
118 
119     private static final Bit TRACE_COPIES = Bit.nextBit(UNIT, "TRACE_COPIES", "TC","FFI ONLY trace copies");
120 
121     private static final Bit TRACE_SKIPPED_COPIES = Bit.nextBit(TRACE_COPIES,"TRACE_SKIPPED_COPIES", "TSC",  "FFI ONLY Trace skipped copies (see MINIMIZE_COPIES) ");
122 
123     private static final Bit TRACE_ENQUEUES = Bit.nextBit(TRACE_SKIPPED_COPIES,"TRACE_ENQUEUES","TE", "FFI ONLY trace enqueued tasks");
124 
125     private static final Bit TRACE_CALLS= Bit.nextBit(TRACE_ENQUEUES, "TRACE_CALLS", "TCALLS","FFI ONLY trace calls (enter/leave)");
126 
127     private static final Bit SHOW_WHY = Bit.nextBit(TRACE_CALLS, "SHOW_WHY", "SW","FFI ONLY show why we decided to copy buffer (H to D)");
128 
129     private static final Bit SHOW_STATE = Bit.nextBit(SHOW_WHY, "SHOW_STATE", "SS","Show iface buffer state changes");
130     public boolean showState(){return SHOW_STATE.isSet(this);}
131 
132     private static final Bit PTX = Bit.nextBit(SHOW_STATE, "PTX", "PTX","FFI (NVIDIA) ONLY pass PTX rather than C99 CUDA code");
133     public boolean ptx(){return PTX.isSet(this);}
134 
135     private static final Bit INTERPRET = Bit.nextBit(PTX, "INTERPRET", "I","Interpret the code model rather than converting to bytecode");
136     public boolean interpret() {
137         return INTERPRET.isSet(this);
138     }
139 
140     private static final Bit HEADLESS = Bit.nextBit(INTERPRET, "HEADLESS", "H","Don't show UI");
141     public boolean headless() {
142         return HEADLESS.isSet(this) || Boolean.getBoolean("headless");
143     }
144     public boolean headless(String arg) {
145         return headless() || "--headless".equals(arg);
146     }
147 
148     private static final Bit SHOW_LOWERED_KERNEL_MODEL = Bit.nextBit(HEADLESS,"SHOW_LOWERED_KERNEL_MODEL", "SLKM","Show (via OpWriter) Lowered Kernel Model");
149     public boolean showLoweredKernelModel() {
150         return SHOW_LOWERED_KERNEL_MODEL.isSet(this);
151     }
152 
153     private static final Bit SHOW_COMPILATION_PHASES = Bit.nextBit(SHOW_LOWERED_KERNEL_MODEL, "SHOW_COMPILATION_PHASES","SCP", "Show HAT compilation phases");
154     public boolean showCompilationPhases() {
155         return SHOW_COMPILATION_PHASES.isSet(this);
156     }
157 
158     private static final Bit SHOW_COMPUTE_MODEL_JAVA_CODE = Bit.nextBit(SHOW_COMPILATION_PHASES, "SHOW_COMPUTE_MODEL_JAVA_CODE","SCMJC", "Show java code view of compute model");
159     public boolean showComputeModelJavaCode() {
160         return SHOW_COMPUTE_MODEL_JAVA_CODE.isSet(this);
161     }
162 
163     public static final Bit CHECK_SSA_LOWERING = Bit.nextBit(SHOW_COMPUTE_MODEL_JAVA_CODE, "CHECK_SSA_LOWERING","CSSAL", "Verify that code model can be lowered to SSA");
164     public boolean checkSSALowering() {
165         return CHECK_SSA_LOWERING.isSet(this);
166     }
167 
168     public static final Bit CUDA_COMPILER_NVCC = Bit.nextBit(CHECK_SSA_LOWERING, "CUDA_NVCC","NVCC", "Select the NVCC CUDA Compiler (Default)");
169     public static final Bit CUDA_COMPILER_NVRTC = Bit.nextBit(CUDA_COMPILER_NVCC, "CUDA_NVRTC","NVRTC", "Select the NVRTC CUDA Compiler");
170 
171     public boolean info() {
172         return INFO.isSet(this);
173     }
174 
175     public static final List<Bit> bitList = List.of(
176             PLATFORM,
177             DEVICE,
178             MINIMIZE_COPIES,
179             TRACE,
180             PROFILE,
181             SHOW_CODE,
182             SHOW_KERNEL_MODEL,
183             SHOW_COMPUTE_MODEL,
184             SHOW_DEVICE_INFO,
185             INFO,
186             WARN,
187             UNIT,
188             TRACE_COPIES,
189             TRACE_SKIPPED_COPIES,
190             TRACE_ENQUEUES,
191             TRACE_CALLS,
192             SHOW_WHY,
193             SHOW_STATE,
194             PTX,
195             INTERPRET,
196             HEADLESS,
197             SHOW_LOWERED_KERNEL_MODEL,
198             SHOW_COMPILATION_PHASES,
199             SHOW_COMPUTE_MODEL_JAVA_CODE,
200             CHECK_SSA_LOWERING,
201             CUDA_COMPILER_NVCC,
202             CUDA_COMPILER_NVRTC
203     );
204 
205     private final int bits;
206 
207     public int bits() {
208         return bits;
209     }
210 
211     Config(int bits) {
212         this.bits = bits;
213     }
214 
215     // These must sync with hat/backends/ffi/shared/include/config.h
216     // We can create the above config by running main() below...
217     public static Config fromEnvOrProperty() {
218         if (System.getenv("HAT") instanceof String opts) {
219             return fromSpec(opts);
220         } else if (System.getProperty("HAT") instanceof String opts) {
221             return fromSpec(opts);
222         } else {
223             return fromSpec("");
224         }
225     }
226 
227     public static Config fromIntBits(int bits) {
228         return new Config(bits);
229     }
230 
231     record BitValue(Bit bit, int value){}
232 
233     public static Config fromSpec(String spec) {
234         Optional<Config> returnValue = Optional.of(Config.fromIntBits(0));
235         if (spec == null || spec.isEmpty()) {
236             // default is good
237         } else if (spec.equals("HELP")) {
238             bitList.forEach(bit -> IO.println(bit.name + "/" + bit.alt + " ".repeat(40 - (bit.name.length() + bit.alt.length())) + (bit.index < 10 ? " " : "") + bit.index + ((bit.size > 1) ? "-" + (bit.index + bit.size - 1) : "  ") + " " + bit.description));
239             if (spec.equals("HELP")) {
240                 System.exit(0);
241             }
242         } else if (spec.contains(",")) {
243             returnValue = Arrays.stream(spec.split(",")).map(Config::fromSpec).reduce((lhs, rhs) -> Config.fromIntBits(lhs.bits() | rhs.bits()));
244         } else if (!spec.contains(":")) {
245             returnValue = bitList.stream().filter(bit -> bit.name().equals(spec) || bit.alt().equals(spec)).findFirst().map(b -> fromIntBits(b.mask()));
246         } else {
247             var split = spec.split(":");
248             if (split.length == 2) {
249                 var optBit = bitList.stream().filter(bit -> bit.name().equals(split[0]) || bit.alt().equals(split[0])).findFirst();
250                 if (optBit.isPresent()) {
251                     int value = Integer.parseInt(split[1]);
252                     // Don't allow devices or platforms outside the supported range
253                     if ((optBit.get().alt.equals("P") || optBit.get().alt.equals("D")) && (value < 0 || value > 7)) {
254                         throw new IllegalArgumentException("Invalid value for " + split[0] + ":" + split[1]);
255                     }
256                     var bv = new BitValue(optBit.get(), Integer.parseInt(split[1]));
257                     var bitz = bv.value << bv.bit().index();
258                     returnValue = Optional.of(fromIntBits(bitz));
259                 }
260             }
261         }
262         if (returnValue.isPresent()) {
263             return returnValue.get();
264         } else {
265             IO.println("Unexpected spec '" + spec + "'");
266             System.exit(1);
267         }
268         return null;
269     }
270 
271     @Override
272     public String toString() {
273         StringBuilder builder = new StringBuilder();
274         for (Bit bit:bitList){
275             if (bit.isBitSet(bits)) {
276                 if (!builder.isEmpty()) {
277                     builder.append("|");
278                 }
279                 builder.append(bit.name());
280             }
281         }
282         return builder.toString();
283     }
284 
285     public static void main() {
286        bitList.forEach(b-> System.out.printf("%30s MASK= %32s\n",  b.name, b.maskString()));
287     }
288 }