Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -697,6 +697,8 @@ HelpText<"Emit Clang AST files for source inputs">; def emit_llvm : Flag<["-"], "emit-llvm">, Flags<[CC1Option]>, Group, HelpText<"Use the LLVM representation for assembler and object files">; +def emit_pch : Flag<["-"], "emit-pch">, Flags<[CC1Option]>, Group, + HelpText<"Generate pre-compiled header file">; def emit_interface_stubs : Flag<["-"], "emit-interface-stubs">, Flags<[CC1Option]>, Group, HelpText<"Generate Interface Stub Files.">; def emit_merged_ifs : Flag<["-"], "emit-merged-ifs">, @@ -4113,8 +4115,6 @@ HelpText<"Generate pre-compiled module file from a C++ module interface">; def emit_header_module : Flag<["-"], "emit-header-module">, HelpText<"Generate pre-compiled module file from a set of header files">; -def emit_pch : Flag<["-"], "emit-pch">, - HelpText<"Generate pre-compiled header file">; def emit_llvm_bc : Flag<["-"], "emit-llvm-bc">, HelpText<"Build ASTs then convert to LLVM, emit .bc file">; def emit_llvm_only : Flag<["-"], "emit-llvm-only">, Index: clang/include/clang/Driver/Types.def =================================================================== --- clang/include/clang/Driver/Types.def +++ clang/include/clang/Driver/Types.def @@ -42,8 +42,8 @@ TYPE("cuda", CUDA, PP_CUDA, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("cuda", CUDA_DEVICE, PP_CUDA, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("hip-cpp-output", PP_HIP, INVALID, "cui", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("hip", HIP, PP_HIP, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("hip", HIP_DEVICE, PP_HIP, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip", HIP, PP_HIP, "hip", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip", HIP_DEVICE, PP_HIP, "hip", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("objective-c-cpp-output", PP_ObjC, INVALID, "mi", phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("objc-cpp-output", PP_ObjC_Alias, INVALID, "mi", phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("objective-c", ObjC, PP_ObjC, "m", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) @@ -66,6 +66,8 @@ TYPE("objective-c++-header", ObjCXXHeader, PP_ObjCXXHeader, "h", phases::Preprocess, phases::Precompile) TYPE("c++-module", CXXModule, PP_CXXModule, "cppm", phases::Preprocess, phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("c++-module-cpp-output", PP_CXXModule, INVALID, "iim", phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip-header-cpp-output", PP_HIPHeader, INVALID, "hipii", phases::Precompile) +TYPE("hip-header", HIPHeader, PP_HIPHeader, "hiphh", phases::Preprocess, phases::Precompile) // Other languages. TYPE("ada", Ada, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -292,7 +292,8 @@ FinalPhase = phases::Preprocess; // --precompile only runs up to precompilation. - } else if ((PhaseArg = DAL.getLastArg(options::OPT__precompile))) { + } else if ((PhaseArg = DAL.getLastArg(options::OPT__precompile)) || + (PhaseArg = DAL.getLastArg(options::OPT_emit_pch))) { FinalPhase = phases::Precompile; // -{fsyntax-only,-analyze,emit-ast} only run up to the compiler. @@ -2394,6 +2395,7 @@ bool CompileDeviceOnly = false; bool EmitLLVM = false; bool EmitAsm = false; + bool EmitPCH = false; /// ID to identify each device compilation. For CUDA it is simply the /// GPU arch string. For HIP it is either the GPU arch string or GPU @@ -2445,7 +2447,8 @@ // If the host input is not CUDA or HIP, we don't need to bother about // this input. if (IA->getType() != types::TY_CUDA && - IA->getType() != types::TY_HIP) { + IA->getType() != types::TY_HIP && + IA->getType() != types::TY_HIPHeader) { // The builder will ignore this input. IsActive = false; return ABRT_Inactive; @@ -2458,8 +2461,11 @@ return ABRT_Success; // Replicate inputs for each GPU architecture. - auto Ty = IA->getType() == types::TY_HIP ? types::TY_HIP_DEVICE - : types::TY_CUDA_DEVICE; + auto Ty = IA->getType(); + if(Ty != types::TY_HIPHeader) + Ty = Ty == types::TY_HIP ? + types::TY_HIP_DEVICE : types::TY_CUDA_DEVICE; + for (unsigned I = 0, E = GpuArchList.size(); I != E; ++I) { CudaDeviceActions.push_back( C.MakeAction(IA->getInputArg(), Ty)); @@ -2584,6 +2590,7 @@ options::OPT_cuda_device_only); EmitLLVM = Args.getLastArg(options::OPT_emit_llvm); EmitAsm = Args.getLastArg(options::OPT_S); + EmitPCH = Args.getLastArg(options::OPT_emit_pch); // Collect all cuda_gpu_arch parameters, removing duplicates. std::set GpuArchs; @@ -2806,6 +2813,24 @@ if (CudaDeviceActions.empty()) return ABRT_Success; + // In EmitPCH mode, construct Phase Actions up to Precompile, + // and ignore phases coming afterwards. + // Otherwise, ignore the Precompile phase. + if (EmitPCH) { + if (CurPhase == phases::Precompile) { + for (Action *&A : CudaDeviceActions) { + A = C.getDriver().ConstructPhaseAction( + C, Args, phases::Precompile, A, AssociatedOffloadKind); + } + return CompileDeviceOnly ? ABRT_Ignore_Host : ABRT_Success; + } + if (CurPhase >= phases::Compile) + return CompileDeviceOnly ? ABRT_Ignore_Host : ABRT_Success; + } else { + if (CurPhase == phases::Precompile) + return ABRT_Success; + } + assert(((CurPhase == phases::Link && Relocatable) || CudaDeviceActions.size() == GpuArchList.size()) && "Expecting one action per GPU architecture."); Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -6318,6 +6318,7 @@ // Disable warnings for clang -E -emit-llvm foo.c Args.ClaimAllArgs(options::OPT_emit_llvm); + Args.ClaimAllArgs(options::OPT_emit_pch); } Clang::Clang(const ToolChain &TC) Index: clang/lib/Driver/Types.cpp =================================================================== --- clang/lib/Driver/Types.cpp +++ clang/lib/Driver/Types.cpp @@ -103,7 +103,7 @@ TY_Plist, TY_RewrittenObjC, TY_RewrittenLegacyObjC, TY_Remap, TY_PCH, TY_Object, TY_Image, TY_dSYM, TY_Dependencies, - TY_CUDA_FATBIN, TY_HIP_FATBIN}; + TY_CUDA_FATBIN, TY_HIP_FATBIN, TY_PP_HIPHeader}; return !llvm::is_contained(kStaticLangageTypes, Id); } @@ -141,6 +141,7 @@ case TY_CXXHeader: case TY_PP_CXXHeader: case TY_ObjCXXHeader: case TY_PP_ObjCXXHeader: case TY_CXXModule: case TY_PP_CXXModule: + case TY_HIPHeader: case TY_PP_HIPHeader: case TY_AST: case TY_ModuleFile: case TY_PCH: case TY_LLVM_IR: case TY_LLVM_BC: return true; @@ -170,10 +171,9 @@ case TY_CXXHeader: case TY_PP_CXXHeader: case TY_ObjCXXHeader: case TY_PP_ObjCXXHeader: case TY_CXXModule: case TY_PP_CXXModule: + case TY_HIPHeader: case TY_PP_HIPHeader: case TY_CUDA: case TY_PP_CUDA: case TY_CUDA_DEVICE: - case TY_HIP: - case TY_PP_HIP: - case TY_HIP_DEVICE: + case TY_HIP: case TY_PP_HIP: case TY_HIP_DEVICE: return true; } } @@ -211,6 +211,7 @@ case TY_HIP: case TY_PP_HIP: case TY_HIP_DEVICE: + case TY_HIPHeader: case TY_PP_HIPHeader: return true; } } @@ -289,6 +290,7 @@ .Case("c++m", TY_CXXModule) .Case("cppm", TY_CXXModule) .Case("cxxm", TY_CXXModule) + .Case("hipp", TY_HIPHeader) .Default(TY_INVALID); } @@ -333,7 +335,8 @@ // --precompile only runs up to precompilation. // This is a clang extension and is not compatible with GCC. - else if (DAL.getLastArg(options::OPT__precompile)) + else if (DAL.getLastArg(options::OPT__precompile) || + DAL.getLastArg(options::OPT_emit_pch)) LastPhase = phases::Precompile; // -{fsyntax-only,-analyze,emit-ast} only run up to the compiler. @@ -395,5 +398,7 @@ return types::TY_ObjCXXHeader; case types::TY_CL: return types::TY_CLHeader; + case types::TY_HIP: + return types::TY_HIPHeader; } } Index: clang/test/Driver/hip-binding.hipp =================================================================== --- /dev/null +++ clang/test/Driver/hip-binding.hipp @@ -0,0 +1,21 @@ +// REQUIRES: clang-driver +// REQUIRES: x86-registered-target +// REQUIRES: amdgpu-registered-target + +// Host only +// RUN: %clang -ccc-print-bindings -target x86_64-linux-gnu \ +// RUN: -x hip-header --cuda-host-only %s \ +// RUN: -emit-pch 2>&1 | FileCheck -check-prefix=PCHH %s +// PCHH: # "x86_64-unknown-linux-gnu" - "clang", inputs: ["{{.*}}hip-binding.hipp"], +// PCHH-SAME: output: "{{.*}}gch" +// +// +// Device only +// RUN: %clang -ccc-print-bindings -target x86_64-linux-gnu \ +// RUN: -x hip-header --cuda-device-only \ +// RUN: --cuda-gpu-arch=gfx803 --cuda-gpu-arch=gfx900 %s \ +// RUN: -emit-pch 2>&1 | FileCheck -check-prefix=PCHD %s +// PCHD: # "amdgcn-amd-amdhsa" - "clang", inputs: ["[[IN:.*hip-binding.hipp]]"], +// PCHD-SAME: output: "{{.*}}gfx803.gch" +// PCHD: # "amdgcn-amd-amdhsa" - "clang", inputs: ["[[IN]]"], +// PCHD-SAME: output: "{{.*}}gfx900.gch" Index: clang/test/Driver/hip-device-compile.hipp =================================================================== --- /dev/null +++ clang/test/Driver/hip-device-compile.hipp @@ -0,0 +1,153 @@ +// REQUIRES: clang-driver +// REQUIRES: x86-registered-target +// REQUIRES: amdgpu-registered-target + +// Host only -emit-pch and -include-pch in compilation +// RUN: %clang -emit-pch --cuda-host-only -### \ +// RUN: -target x86_64-linux-gnu -x hip-header \ +// RUN: -nogpulib -o a.hipp.pch \ +// RUN: %S/Inputs/hip_multiple_inputs/a.hipp \ +// RUN: 2>&1 | FileCheck -check-prefixes=HOSTP %s +// +// RUN: %clang -x hip --cuda-host-only -### \ +// RUN: -target x86_64-linux-gnu -include-pch a.hipp.pch \ +// RUN: -nogpulib -c -emit-llvm -o b.bc \ +// RUN: %S/Inputs/hip_multiple_inputs/b.hip \ +// RUN: 2>&1 | FileCheck -check-prefixes=HOSTC %s +// +// HOSTP: {{".*clang.*"}} "-cc1" "-triple" "x86_64-unknown-linux-gnu" +// HOSTP-SAME: "-aux-triple" "amdgcn-amd-amdhsa" +// HOSTP-SAME: "-emit-pch" +// HOSTP-SAME: "-main-file-name" "a.hipp" +// HOSTP-SAME: "-o" "a.hipp.pch" +// HOSTP-SAME: {{".*a.hipp"}} +// +// HOSTC: {{".*clang.*"}} "-cc1" "-triple" "x86_64-unknown-linux-gnu" +// HOSTC-SAME: "-aux-triple" "amdgcn-amd-amdhsa" +// HOSTC-SAME: "-emit-llvm-bc" +// HOSTC-SAME: "-main-file-name" "b.hip" +// HOSTC-SAME: "-o" "b.bc" +// HOSTC-SAME: {{".*b.hip"}} + +// Single Device -emit-pch and -include-pch in compilation +// RUN: %clang -emit-pch --cuda-device-only -### \ +// RUN: -target x86_64-linux-gnu -o a.hipp.pch -x hip-header \ +// RUN: --cuda-gpu-arch=gfx900 -nogpulib \ +// RUN: %S/Inputs/hip_multiple_inputs/a.hipp \ +// RUN: 2>&1 | FileCheck -check-prefixes=DEV1P %s +// +// RUN: %clang -x hip --cuda-device-only -### \ +// RUN: -target x86_64-linux-gnu -include-pch a.hipp.pch \ +// RUN: -c -emit-llvm -o b.bc \ +// RUN: --cuda-gpu-arch=gfx900 -nogpulib \ +// RUN: %S/Inputs/hip_multiple_inputs/b.hip \ +// RUN: 2>&1 | FileCheck -check-prefixes=DEV1C %s +// +// DEV1P: {{".*clang.*"}} "-cc1" "-triple" "amdgcn-amd-amdhsa" +// DEV1P-SAME: "-aux-triple" "x86_64-unknown-linux-gnu" +// DEV1P-SAME: "-emit-pch" +// DEV1P-SAME: "-main-file-name" "a.hipp" +// DEV1P-SAME: "-fcuda-is-device" +// DEV1P-SAME: "-target-cpu" "gfx900" +// DEV1P-SAME: "-o" "a.hipp.pch" +// DEV1P-SAME: {{".*a.hipp"}} +// +// DEV1P-NOT: {{"*.llvm-link"}} +// DEV1P-NOT: {{".*opt"}} +// DEV1P-NOT: {{".*llc"}} +// DEV1P-NOT: {{".*lld.*"}} +// DEV1P-NOT: {{".*clang-offload-bundler"}} +// DEV1P-NOT: {{".*ld.*"}} +// +// DEV1C: {{".*clang.*"}} "-cc1" "-triple" "amdgcn-amd-amdhsa" +// DEV1C-SAME: "-aux-triple" "x86_64-unknown-linux-gnu" +// DEV1C-SAME: "-emit-llvm-bc" +// DEV1C-SAME: "-main-file-name" "b.hip" +// DEV1C-SAME: "-fcuda-is-device" +// DEV1C-SAME: "-target-cpu" "gfx900" +// DEV1C-SAME: "-include-pch" "a.hipp.pch" +// DEV1C-SAME: "-o" "b.bc" +// DEV1C-SAME: {{".*b.hip"}} + +// Multi Device -emit-pch and -include-pch in compilation +// RUN: %clang -emit-pch --cuda-device-only -### \ +// RUN: -target x86_64-linux-gnu -x hip-header -nogpulib \ +// RUN: --cuda-gpu-arch=gfx803 --cuda-gpu-arch=gfx900 \ +// RUN: %S/Inputs/hip_multiple_inputs/a.hipp \ +// RUN: 2>&1 | FileCheck -check-prefixes=DEV2P %s +// +// RUN: %clang -x hip --cuda-device-only -### \ +// RUN: -target x86_64-linux-gnu -c -emit-llvm \ +// RUN: -include-pch a.hipp-hip-amdgcn-amd-amdhsa-gfx803.gch \ +// RUN: --cuda-gpu-arch=gfx803 -nogpulib -o a.bc \ +// RUN: %S/Inputs/hip_multiple_inputs/a.cu \ +// RUN: 2>&1 | FileCheck -check-prefixes=DEV2C8 %s +// +// RUN: %clang -x hip --cuda-device-only -### \ +// RUN: -target x86_64-linux-gnu -c -emit-llvm \ +// RUN: -include-pch a.hipp-hip-amdgcn-amd-amdhsa-gfx900.gch \ +// RUN: --cuda-gpu-arch=gfx900 -nogpulib -o b.bc \ +// RUN: %S/Inputs/hip_multiple_inputs/b.hip \ +// RUN: 2>&1 | FileCheck -check-prefixes=DEV2C9 %s +// +// DEV2P: {{".*clang.*"}} "-cc1" "-triple" "amdgcn-amd-amdhsa" +// DEV2P-SAME: "-aux-triple" "x86_64-unknown-linux-gnu" +// DEV2P-SAME: "-emit-pch" +// DEV2P-SAME: "-main-file-name" "a.hipp" +// DEV2P-SAME: "-fcuda-is-device" +// DEV2P-SAME: "-target-cpu" "gfx803" +// DEV2P-SAME: "-o" "{{.*gfx803.gch}}" +// DEV2P-SAME: {{".*a.hipp"}} +// +// DEV2P: {{".*clang.*"}} "-cc1" "-triple" "amdgcn-amd-amdhsa" +// DEV2P-SAME: "-aux-triple" "x86_64-unknown-linux-gnu" +// DEV2P-SAME: "-emit-pch" +// DEV2P-SAME: "-main-file-name" "a.hipp" +// DEV2P-SAME: "-fcuda-is-device" +// DEV2P-SAME: "-target-cpu" "gfx900" +// DEV2P-SAME: "-o" "{{.*gfx900.gch}}" +// DEV2P-SAME: {{".*a.hipp"}} +// +// DEV2P-NOT: {{"*.llvm-link"}} +// DEV2P-NOT: {{".*opt"}} +// DEV2P-NOT: {{".*llc"}} +// DEV2P-NOT: {{".*lld.*"}} +// DEV2P-NOT: {{".*clang-offload-bundler"}} +// DEV2P-NOT: {{".*ld.*"}} +// +// DEV2C8: {{".*clang.*"}} "-cc1" "-triple" "amdgcn-amd-amdhsa" +// DEV2C8-SAME: "-aux-triple" "x86_64-unknown-linux-gnu" +// DEV2C8-SAME: "-emit-llvm-bc" +// DEV2C8-SAME: "-main-file-name" "a.cu" +// DEV2C8-SAME: "-fcuda-is-device" +// DEV2C8-SAME: "-target-cpu" "gfx803" +// DEV2C8-SAME: "-include-pch" "a.hipp-hip-amdgcn-amd-amdhsa-gfx803.gch" +// DEV2C8-SAME: "-o" "a.bc" +// DEV2C8-SAME: {{".*a.cu"}} +// +// DEV2C9: {{".*clang.*"}} "-cc1" "-triple" "amdgcn-amd-amdhsa" +// DEV2C9-SAME: "-aux-triple" "x86_64-unknown-linux-gnu" +// DEV2C9-SAME: "-emit-llvm-bc" +// DEV2C9-SAME: "-main-file-name" "b.hip" +// DEV2C9-SAME: "-fcuda-is-device" +// DEV2C9-SAME: "-target-cpu" "gfx900" +// DEV2C9-SAME: "-include-pch" "a.hipp-hip-amdgcn-amd-amdhsa-gfx900.gch" +// DEV2C9-SAME: "-o" "b.bc" +// DEV2C9-SAME: {{".*b.hip"}} + +// Compiling with the wrong GPU variant of PCH should fail +// RUN: %clang -emit-pch --cuda-device-only -v \ +// RUN: -target x86_64-linux-gnu -x hip-header -nogpulib \ +// RUN: --cuda-gpu-arch=gfx900 -o a-900.hipp.pch \ +// RUN: %S/Inputs/hip_multiple_inputs/a.hipp \ +// RUN: 2>&1 | FileCheck -check-prefixes=CREATE %s +// CREATE: {{.*}} -o a-900.hipp.pch +// +// RUN: not %clang -x hip --cuda-device-only \ +// RUN: -target x86_64-linux-gnu -c -emit-llvm \ +// RUN: -include-pch a-900.hipp.pch \ +// RUN: --cuda-gpu-arch=gfx803 -nogpulib -o b.bc \ +// RUN: %S/Inputs/hip_multiple_inputs/b.hip \ +// RUN: 2>&1 | FileCheck -check-prefixes=ERROR --implicit-check-not=error: %s +// +// ERROR: error: PCH file was compiled for the target CPU 'gfx900' but the current translation unit is being compiled for target 'gfx803' Index: clang/test/Driver/hip-phases.hipp =================================================================== --- /dev/null +++ clang/test/Driver/hip-phases.hipp @@ -0,0 +1,41 @@ +// REQUIRES: clang-driver +// REQUIRES: x86-registered-target +// REQUIRES: amdgpu-registered-target +// +// +// Test single gpu architecture up to the precompile phase in device-only mode. +// +// RUN: %clang -x hip-header -target x86_64-unknown-linux-gnu -ccc-print-phases \ +// RUN: --cuda-gpu-arch=gfx803 %s --cuda-device-only -emit-pch 2>&1 \ +// RUN: | FileCheck -check-prefixes=PCH %s +// +// PCH-DAG: [[P0:[0-9]+]]: input, "{{.*}}hip-phases.hipp", [[T:hip-header]], (device-hip, [[ARCH:gfx803]]) +// PCH-DAG: [[P1:[0-9]+]]: preprocessor, {[[P0]]}, [[T]]-cpp-output, (device-hip, [[ARCH]]) +// PCH-DAG: [[P2:[0-9]+]]: precompiler, {[[P1]]}, precompiled-header, (device-hip, [[ARCH]]) +// PCH-NOT: host +// +// Test two gpu architecture up to the precompile phase in device-only mode. +// +// RUN: %clang -x hip-header -target x86_64-unknown-linux-gnu \ +// RUN: -ccc-print-phases --cuda-gpu-arch=gfx803 --cuda-gpu-arch=gfx900 %s \ +// RUN: --cuda-device-only -emit-pch 2>&1 \ +// RUN: | FileCheck -check-prefixes=PCH2 %s +// +// PCH2-DAG: [[P0:[0-9]+]]: input, "{{.*}}hip-phases.hipp", [[T:hip-header]], (device-hip, [[ARCH:gfx803]]) +// PCH2-DAG: [[P1:[0-9]+]]: preprocessor, {[[P0]]}, [[T]]-cpp-output, (device-hip, [[ARCH]]) +// PCH2-DAG: [[P2:[0-9]+]]: precompiler, {[[P1]]}, precompiled-header, (device-hip, [[ARCH]]) +// PCH2-DAG: [[P4:[0-9]+]]: input, "{{.*}}hip-phases.hipp", [[T]], (device-hip, [[ARCH:gfx900]]) +// PCH2-DAG: [[P5:[0-9]+]]: preprocessor, {[[P4]]}, [[T]]-cpp-output, (device-hip, [[ARCH]]) +// PCH2-DAG: [[P6:[0-9]+]]: precompiler, {[[P5]]}, precompiled-header, (device-hip, [[ARCH]]) +// PCH2-NOT: host +// +// Test the precompile phase in host-only mode. +// +// RUN: %clang -x hip-header -target x86_64-unknown-linux-gnu \ +// RUN: -ccc-print-phases %s --cuda-host-only \ +// RUN: -emit-pch 2>&1 | FileCheck -check-prefixes=PCHH %s +// +// PCHH-DAG: [[P0:[0-9]+]]: input, "{{.*}}hip-phases.hipp", [[T:hip-header]], (host-hip) +// PCHH-DAG: [[P1:[0-9]+]]: preprocessor, {[[P0]]}, [[T]]-cpp-output, (host-hip) +// PCHH-DAG: [[P2:[0-9]+]]: precompiler, {[[P1]]}, precompiled-header, (host-hip) +// PCHH-NOT: device