Index: clang/include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticDriverKinds.td +++ clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -57,6 +57,8 @@ "standard library not linked and so no interrupt vector table or " "compiler runtime routines will be linked">, InGroup; +def err_drv_avr_assembly_only : Error< + "device '%0' only supports assembly programming">; def err_drv_cuda_bad_gpu_arch : Error<"unsupported CUDA gpu architecture: %0">; def err_drv_no_cuda_installation : Error< "cannot find CUDA installation; provide its path via '--cuda-path', or pass " Index: clang/lib/Driver/ToolChains/AVR.h =================================================================== --- clang/lib/Driver/ToolChains/AVR.h +++ clang/lib/Driver/ToolChains/AVR.h @@ -19,6 +19,8 @@ namespace toolchains { class LLVM_LIBRARY_VISIBILITY AVRToolChain : public Generic_ELF { + std::string AVRMcu; + public: AVRToolChain(const Driver &D, const llvm::Triple &Triple, const llvm::opt::ArgList &Args); Index: clang/lib/Driver/ToolChains/AVR.cpp =================================================================== --- clang/lib/Driver/ToolChains/AVR.cpp +++ clang/lib/Driver/ToolChains/AVR.cpp @@ -12,6 +12,7 @@ #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/InputInfo.h" #include "clang/Driver/Options.h" +#include "clang/Driver/Types.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" @@ -19,6 +20,7 @@ #include "llvm/MC/SubtargetFeature.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" using namespace clang::driver; using namespace clang::driver::toolchains; @@ -311,24 +313,27 @@ : Generic_ELF(D, Triple, Args), LinkStdlib(false) { GCCInstallation.init(Triple, Args); + // Save the CPU name for further checks. + AVRMcu = getCPUName(D, Args, Triple); + // Only add default libraries if the user hasn't explicitly opted out. if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs) && + !Args.hasArg(options::OPT_S /* does not apply when not linking */) && !Args.hasArg(options::OPT_c /* does not apply when not linking */)) { - std::string CPU = getCPUName(D, Args, Triple); - if (CPU.empty()) { + if (AVRMcu.empty()) { // We cannot link any standard libraries without an MCU specified. D.Diag(diag::warn_drv_avr_mcu_not_specified); } else { - Optional FamilyName = GetMCUFamilyName(CPU); + Optional FamilyName = GetMCUFamilyName(AVRMcu); Optional AVRLibcRoot = findAVRLibcInstallation(); if (!FamilyName.hasValue()) { // We do not have an entry for this CPU in the family // mapping table yet. D.Diag(diag::warn_drv_avr_family_linking_stdlibs_not_implemented) - << CPU; + << AVRMcu; } else if (!GCCInstallation.isValid()) { // No avr-gcc found and so no runtime linked. D.Diag(diag::warn_drv_avr_gcc_not_found); @@ -339,7 +344,7 @@ std::string GCCRoot(GCCInstallation.getInstallPath()); std::string GCCParentPath(GCCInstallation.getParentLibPath()); std::string LibcRoot = AVRLibcRoot.getValue(); - std::string SubPath = GetMCUSubPath(CPU); + std::string SubPath = GetMCUSubPath(AVRMcu); getProgramPaths().push_back(GCCParentPath + "/../bin"); getFilePaths().push_back(LibcRoot + std::string("/lib/") + SubPath); @@ -379,6 +384,47 @@ if (!DriverArgs.hasFlag(options::OPT_fuse_init_array, options::OPT_fno_use_init_array, false)) CC1Args.push_back("-fno-use-init-array"); + + // Reject non assembly source files for devices in the avr1 family. + // FIXME: Enable avrtiny when the following bug is fixed: + // https://github.com/llvm/llvm-project/issues/53459 + llvm::Optional FamilyName = GetMCUFamilyName(AVRMcu); + if (FamilyName.hasValue() && + (FamilyName->equals("avr1") || FamilyName->equals("avrtiny"))) { + // Only '-x assembler-with-cpp' or '-x assembler' is allowed for avr1. + auto Langs = DriverArgs.getAllArgValues(options::OPT_x); + for (auto Lang : Langs) + if (Lang != "assembler-with-cpp" && Lang != "assembler") + getDriver().Diag(diag::err_drv_avr_assembly_only) << AVRMcu; + + // Get the main file name. + const char *MainFileName = nullptr; + for (unsigned I = 0, J = CC1Args.size(); I < J; I++) + if (StringRef(CC1Args[I]).equals("-main-file-name")) { + MainFileName = CC1Args[I + 1]; + break; + } + if (!MainFileName) + return; + + // The main input file should be assembly only, it is OK that the main + // input file has either a '.S' or a '.s' suffix. + types::ID MainFileId = types::lookupTypeForExtension( + llvm::sys::path::extension(MainFileName).drop_front()); + if (MainFileId == types::TY_Asm || MainFileId == types::TY_PP_Asm) + return; + + // The main input file should be assembly only, it should be led by either + // '-x assembler-with-cpp' or '-x assembler', if it has neither a '.S' nor + // a '.s' suffix. + for (unsigned I = 0, J = DriverArgs.size(); I < J; I++) { + StringRef Arg(DriverArgs.getArgString(I)); + if (Arg.equals("assembler-with-cpp") || Arg.equals("assembler")) + return; + if (Arg.contains(MainFileName)) + getDriver().Diag(diag::err_drv_avr_assembly_only) << AVRMcu; + } + } } Tool *AVRToolChain::buildLinker() const { Index: clang/test/Driver/avr-mmcu.S =================================================================== --- /dev/null +++ clang/test/Driver/avr-mmcu.S @@ -0,0 +1,13 @@ +// A test for the supported language mode by different avr families. + +// RUN: %clang -### -target avr -no-canonical-prefixes -mmcu=attiny11 %s -c 2>&1 | FileCheck -check-prefix=CHECKA %s +// CHECKA-NOT: error: device 'attiny11' only supports assembly programming + +// RUN: %clang -### -target avr -no-canonical-prefixes -mmcu=attiny11 -x c++ %s -c 2>&1 | FileCheck -check-prefix=CHECKB %s +// CHECKB: error: device 'attiny11' only supports assembly programming + +// RUN: %clang -### -target avr -no-canonical-prefixes -mmcu=at90s8515 %s -c 2>&1 | FileCheck -check-prefix=CHECKC %s +// CHECKC-NOT: error: device 'at90s8515' only supports assembly programming + +// RUN: %clang -### -target avr -no-canonical-prefixes -mmcu=at90s8515 %s -x c++ -c 2>&1 | FileCheck -check-prefix=CHECKD %s +// CHECKD-NOT: error: device 'at90s8515' only supports assembly programming Index: clang/test/Driver/avr-mmcu.c =================================================================== --- clang/test/Driver/avr-mmcu.c +++ clang/test/Driver/avr-mmcu.c @@ -1,12 +1,15 @@ // A test for the propagation of the -mmcu option to -cc1 and -cc1as -// RUN: %clang -### -target avr -no-canonical-prefixes -mmcu=attiny11 -save-temps %s 2>&1 | FileCheck -check-prefix=CHECK0 %s -// CHECK0: clang{{.*}} "-cc1" {{.*}} "-target-cpu" "attiny11" -// CHECK0: clang{{.*}} "-cc1as" {{.*}} "-target-cpu" "attiny11" +// RUN: %clang -### -target avr -no-canonical-prefixes -mmcu=attiny11 %s -c 2>&1 | FileCheck -check-prefix=CHECKA %s +// CHECKA: error: device 'attiny11' only supports assembly programming + +// RUN: %clang -### -target avr -no-canonical-prefixes -mmcu=attiny11 -x assembler-with-cpp %s -c 2>&1 | FileCheck -check-prefix=CHECKB %s +// CHECKB-NOT: error: device 'attiny11' only supports assembly programming // RUN: %clang -### -target avr -no-canonical-prefixes -mmcu=at90s2313 -save-temps %s 2>&1 | FileCheck -check-prefix=CHECK1 %s // CHECK1: clang{{.*}} "-cc1" {{.*}} "-target-cpu" "at90s2313" // CHECK1: clang{{.*}} "-cc1as" {{.*}} "-target-cpu" "at90s2313" +// CHECK1-NOT: error: device 'at90s2313' only supports assembly programming // RUN: %clang -### -target avr -no-canonical-prefixes -mmcu=at90s8515 -save-temps %s 2>&1 | FileCheck -check-prefix=CHECK2 %s // CHECK2: clang{{.*}} "-cc1" {{.*}} "-target-cpu" "at90s8515"