| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,240 @@ | ||
| //===------ SemaMIPS.cpp -------- MIPS target-specific routines -----------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This file implements semantic analysis functions specific to MIPS. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "clang/Sema/SemaMIPS.h" | ||
| #include "clang/Basic/DiagnosticSema.h" | ||
| #include "clang/Basic/TargetBuiltins.h" | ||
| #include "clang/Sema/Sema.h" | ||
|
|
||
| namespace clang { | ||
|
|
||
| SemaMIPS::SemaMIPS(Sema &S) : SemaBase(S) {} | ||
|
|
||
| bool SemaMIPS::CheckMipsBuiltinFunctionCall(const TargetInfo &TI, | ||
| unsigned BuiltinID, | ||
| CallExpr *TheCall) { | ||
| return CheckMipsBuiltinCpu(TI, BuiltinID, TheCall) || | ||
| CheckMipsBuiltinArgument(BuiltinID, TheCall); | ||
| } | ||
|
|
||
| bool SemaMIPS::CheckMipsBuiltinCpu(const TargetInfo &TI, unsigned BuiltinID, | ||
| CallExpr *TheCall) { | ||
|
|
||
| if (Mips::BI__builtin_mips_addu_qb <= BuiltinID && | ||
| BuiltinID <= Mips::BI__builtin_mips_lwx) { | ||
| if (!TI.hasFeature("dsp")) | ||
| return Diag(TheCall->getBeginLoc(), diag::err_mips_builtin_requires_dsp); | ||
| } | ||
|
|
||
| if (Mips::BI__builtin_mips_absq_s_qb <= BuiltinID && | ||
| BuiltinID <= Mips::BI__builtin_mips_subuh_r_qb) { | ||
| if (!TI.hasFeature("dspr2")) | ||
| return Diag(TheCall->getBeginLoc(), | ||
| diag::err_mips_builtin_requires_dspr2); | ||
| } | ||
|
|
||
| if (Mips::BI__builtin_msa_add_a_b <= BuiltinID && | ||
| BuiltinID <= Mips::BI__builtin_msa_xori_b) { | ||
| if (!TI.hasFeature("msa")) | ||
| return Diag(TheCall->getBeginLoc(), diag::err_mips_builtin_requires_msa); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| // CheckMipsBuiltinArgument - Checks the constant value passed to the | ||
| // intrinsic is correct. The switch statement is ordered by DSP, MSA. The | ||
| // ordering for DSP is unspecified. MSA is ordered by the data format used | ||
| // by the underlying instruction i.e., df/m, df/n and then by size. | ||
| // | ||
| // FIXME: The size tests here should instead be tablegen'd along with the | ||
| // definitions from include/clang/Basic/BuiltinsMips.def. | ||
| // FIXME: GCC is strict on signedness for some of these intrinsics, we should | ||
| // be too. | ||
| bool SemaMIPS::CheckMipsBuiltinArgument(unsigned BuiltinID, CallExpr *TheCall) { | ||
| unsigned i = 0, l = 0, u = 0, m = 0; | ||
| switch (BuiltinID) { | ||
| default: return false; | ||
| case Mips::BI__builtin_mips_wrdsp: i = 1; l = 0; u = 63; break; | ||
| case Mips::BI__builtin_mips_rddsp: i = 0; l = 0; u = 63; break; | ||
| case Mips::BI__builtin_mips_append: i = 2; l = 0; u = 31; break; | ||
| case Mips::BI__builtin_mips_balign: i = 2; l = 0; u = 3; break; | ||
| case Mips::BI__builtin_mips_precr_sra_ph_w: i = 2; l = 0; u = 31; break; | ||
| case Mips::BI__builtin_mips_precr_sra_r_ph_w: i = 2; l = 0; u = 31; break; | ||
| case Mips::BI__builtin_mips_prepend: i = 2; l = 0; u = 31; break; | ||
| // MSA intrinsics. Instructions (which the intrinsics maps to) which use the | ||
| // df/m field. | ||
| // These intrinsics take an unsigned 3 bit immediate. | ||
| case Mips::BI__builtin_msa_bclri_b: | ||
| case Mips::BI__builtin_msa_bnegi_b: | ||
| case Mips::BI__builtin_msa_bseti_b: | ||
| case Mips::BI__builtin_msa_sat_s_b: | ||
| case Mips::BI__builtin_msa_sat_u_b: | ||
| case Mips::BI__builtin_msa_slli_b: | ||
| case Mips::BI__builtin_msa_srai_b: | ||
| case Mips::BI__builtin_msa_srari_b: | ||
| case Mips::BI__builtin_msa_srli_b: | ||
| case Mips::BI__builtin_msa_srlri_b: i = 1; l = 0; u = 7; break; | ||
| case Mips::BI__builtin_msa_binsli_b: | ||
| case Mips::BI__builtin_msa_binsri_b: i = 2; l = 0; u = 7; break; | ||
| // These intrinsics take an unsigned 4 bit immediate. | ||
| case Mips::BI__builtin_msa_bclri_h: | ||
| case Mips::BI__builtin_msa_bnegi_h: | ||
| case Mips::BI__builtin_msa_bseti_h: | ||
| case Mips::BI__builtin_msa_sat_s_h: | ||
| case Mips::BI__builtin_msa_sat_u_h: | ||
| case Mips::BI__builtin_msa_slli_h: | ||
| case Mips::BI__builtin_msa_srai_h: | ||
| case Mips::BI__builtin_msa_srari_h: | ||
| case Mips::BI__builtin_msa_srli_h: | ||
| case Mips::BI__builtin_msa_srlri_h: i = 1; l = 0; u = 15; break; | ||
| case Mips::BI__builtin_msa_binsli_h: | ||
| case Mips::BI__builtin_msa_binsri_h: i = 2; l = 0; u = 15; break; | ||
| // These intrinsics take an unsigned 5 bit immediate. | ||
| // The first block of intrinsics actually have an unsigned 5 bit field, | ||
| // not a df/n field. | ||
| case Mips::BI__builtin_msa_cfcmsa: | ||
| case Mips::BI__builtin_msa_ctcmsa: i = 0; l = 0; u = 31; break; | ||
| case Mips::BI__builtin_msa_clei_u_b: | ||
| case Mips::BI__builtin_msa_clei_u_h: | ||
| case Mips::BI__builtin_msa_clei_u_w: | ||
| case Mips::BI__builtin_msa_clei_u_d: | ||
| case Mips::BI__builtin_msa_clti_u_b: | ||
| case Mips::BI__builtin_msa_clti_u_h: | ||
| case Mips::BI__builtin_msa_clti_u_w: | ||
| case Mips::BI__builtin_msa_clti_u_d: | ||
| case Mips::BI__builtin_msa_maxi_u_b: | ||
| case Mips::BI__builtin_msa_maxi_u_h: | ||
| case Mips::BI__builtin_msa_maxi_u_w: | ||
| case Mips::BI__builtin_msa_maxi_u_d: | ||
| case Mips::BI__builtin_msa_mini_u_b: | ||
| case Mips::BI__builtin_msa_mini_u_h: | ||
| case Mips::BI__builtin_msa_mini_u_w: | ||
| case Mips::BI__builtin_msa_mini_u_d: | ||
| case Mips::BI__builtin_msa_addvi_b: | ||
| case Mips::BI__builtin_msa_addvi_h: | ||
| case Mips::BI__builtin_msa_addvi_w: | ||
| case Mips::BI__builtin_msa_addvi_d: | ||
| case Mips::BI__builtin_msa_bclri_w: | ||
| case Mips::BI__builtin_msa_bnegi_w: | ||
| case Mips::BI__builtin_msa_bseti_w: | ||
| case Mips::BI__builtin_msa_sat_s_w: | ||
| case Mips::BI__builtin_msa_sat_u_w: | ||
| case Mips::BI__builtin_msa_slli_w: | ||
| case Mips::BI__builtin_msa_srai_w: | ||
| case Mips::BI__builtin_msa_srari_w: | ||
| case Mips::BI__builtin_msa_srli_w: | ||
| case Mips::BI__builtin_msa_srlri_w: | ||
| case Mips::BI__builtin_msa_subvi_b: | ||
| case Mips::BI__builtin_msa_subvi_h: | ||
| case Mips::BI__builtin_msa_subvi_w: | ||
| case Mips::BI__builtin_msa_subvi_d: i = 1; l = 0; u = 31; break; | ||
| case Mips::BI__builtin_msa_binsli_w: | ||
| case Mips::BI__builtin_msa_binsri_w: i = 2; l = 0; u = 31; break; | ||
| // These intrinsics take an unsigned 6 bit immediate. | ||
| case Mips::BI__builtin_msa_bclri_d: | ||
| case Mips::BI__builtin_msa_bnegi_d: | ||
| case Mips::BI__builtin_msa_bseti_d: | ||
| case Mips::BI__builtin_msa_sat_s_d: | ||
| case Mips::BI__builtin_msa_sat_u_d: | ||
| case Mips::BI__builtin_msa_slli_d: | ||
| case Mips::BI__builtin_msa_srai_d: | ||
| case Mips::BI__builtin_msa_srari_d: | ||
| case Mips::BI__builtin_msa_srli_d: | ||
| case Mips::BI__builtin_msa_srlri_d: i = 1; l = 0; u = 63; break; | ||
| case Mips::BI__builtin_msa_binsli_d: | ||
| case Mips::BI__builtin_msa_binsri_d: i = 2; l = 0; u = 63; break; | ||
| // These intrinsics take a signed 5 bit immediate. | ||
| case Mips::BI__builtin_msa_ceqi_b: | ||
| case Mips::BI__builtin_msa_ceqi_h: | ||
| case Mips::BI__builtin_msa_ceqi_w: | ||
| case Mips::BI__builtin_msa_ceqi_d: | ||
| case Mips::BI__builtin_msa_clti_s_b: | ||
| case Mips::BI__builtin_msa_clti_s_h: | ||
| case Mips::BI__builtin_msa_clti_s_w: | ||
| case Mips::BI__builtin_msa_clti_s_d: | ||
| case Mips::BI__builtin_msa_clei_s_b: | ||
| case Mips::BI__builtin_msa_clei_s_h: | ||
| case Mips::BI__builtin_msa_clei_s_w: | ||
| case Mips::BI__builtin_msa_clei_s_d: | ||
| case Mips::BI__builtin_msa_maxi_s_b: | ||
| case Mips::BI__builtin_msa_maxi_s_h: | ||
| case Mips::BI__builtin_msa_maxi_s_w: | ||
| case Mips::BI__builtin_msa_maxi_s_d: | ||
| case Mips::BI__builtin_msa_mini_s_b: | ||
| case Mips::BI__builtin_msa_mini_s_h: | ||
| case Mips::BI__builtin_msa_mini_s_w: | ||
| case Mips::BI__builtin_msa_mini_s_d: i = 1; l = -16; u = 15; break; | ||
| // These intrinsics take an unsigned 8 bit immediate. | ||
| case Mips::BI__builtin_msa_andi_b: | ||
| case Mips::BI__builtin_msa_nori_b: | ||
| case Mips::BI__builtin_msa_ori_b: | ||
| case Mips::BI__builtin_msa_shf_b: | ||
| case Mips::BI__builtin_msa_shf_h: | ||
| case Mips::BI__builtin_msa_shf_w: | ||
| case Mips::BI__builtin_msa_xori_b: i = 1; l = 0; u = 255; break; | ||
| case Mips::BI__builtin_msa_bseli_b: | ||
| case Mips::BI__builtin_msa_bmnzi_b: | ||
| case Mips::BI__builtin_msa_bmzi_b: i = 2; l = 0; u = 255; break; | ||
| // df/n format | ||
| // These intrinsics take an unsigned 4 bit immediate. | ||
| case Mips::BI__builtin_msa_copy_s_b: | ||
| case Mips::BI__builtin_msa_copy_u_b: | ||
| case Mips::BI__builtin_msa_insve_b: | ||
| case Mips::BI__builtin_msa_splati_b: i = 1; l = 0; u = 15; break; | ||
| case Mips::BI__builtin_msa_sldi_b: i = 2; l = 0; u = 15; break; | ||
| // These intrinsics take an unsigned 3 bit immediate. | ||
| case Mips::BI__builtin_msa_copy_s_h: | ||
| case Mips::BI__builtin_msa_copy_u_h: | ||
| case Mips::BI__builtin_msa_insve_h: | ||
| case Mips::BI__builtin_msa_splati_h: i = 1; l = 0; u = 7; break; | ||
| case Mips::BI__builtin_msa_sldi_h: i = 2; l = 0; u = 7; break; | ||
| // These intrinsics take an unsigned 2 bit immediate. | ||
| case Mips::BI__builtin_msa_copy_s_w: | ||
| case Mips::BI__builtin_msa_copy_u_w: | ||
| case Mips::BI__builtin_msa_insve_w: | ||
| case Mips::BI__builtin_msa_splati_w: i = 1; l = 0; u = 3; break; | ||
| case Mips::BI__builtin_msa_sldi_w: i = 2; l = 0; u = 3; break; | ||
| // These intrinsics take an unsigned 1 bit immediate. | ||
| case Mips::BI__builtin_msa_copy_s_d: | ||
| case Mips::BI__builtin_msa_copy_u_d: | ||
| case Mips::BI__builtin_msa_insve_d: | ||
| case Mips::BI__builtin_msa_splati_d: i = 1; l = 0; u = 1; break; | ||
| case Mips::BI__builtin_msa_sldi_d: i = 2; l = 0; u = 1; break; | ||
| // Memory offsets and immediate loads. | ||
| // These intrinsics take a signed 10 bit immediate. | ||
| case Mips::BI__builtin_msa_ldi_b: i = 0; l = -128; u = 255; break; | ||
| case Mips::BI__builtin_msa_ldi_h: | ||
| case Mips::BI__builtin_msa_ldi_w: | ||
| case Mips::BI__builtin_msa_ldi_d: i = 0; l = -512; u = 511; break; | ||
| case Mips::BI__builtin_msa_ld_b: i = 1; l = -512; u = 511; m = 1; break; | ||
| case Mips::BI__builtin_msa_ld_h: i = 1; l = -1024; u = 1022; m = 2; break; | ||
| case Mips::BI__builtin_msa_ld_w: i = 1; l = -2048; u = 2044; m = 4; break; | ||
| case Mips::BI__builtin_msa_ld_d: i = 1; l = -4096; u = 4088; m = 8; break; | ||
| case Mips::BI__builtin_msa_ldr_d: i = 1; l = -4096; u = 4088; m = 8; break; | ||
| case Mips::BI__builtin_msa_ldr_w: i = 1; l = -2048; u = 2044; m = 4; break; | ||
| case Mips::BI__builtin_msa_st_b: i = 2; l = -512; u = 511; m = 1; break; | ||
| case Mips::BI__builtin_msa_st_h: i = 2; l = -1024; u = 1022; m = 2; break; | ||
| case Mips::BI__builtin_msa_st_w: i = 2; l = -2048; u = 2044; m = 4; break; | ||
| case Mips::BI__builtin_msa_st_d: i = 2; l = -4096; u = 4088; m = 8; break; | ||
| case Mips::BI__builtin_msa_str_d: i = 2; l = -4096; u = 4088; m = 8; break; | ||
| case Mips::BI__builtin_msa_str_w: i = 2; l = -2048; u = 2044; m = 4; break; | ||
| } | ||
|
|
||
| if (!m) | ||
| return SemaRef.BuiltinConstantArgRange(TheCall, i, l, u); | ||
|
|
||
| return SemaRef.BuiltinConstantArgRange(TheCall, i, l, u) || | ||
| SemaRef.BuiltinConstantArgMultiple(TheCall, i, m); | ||
| } | ||
|
|
||
| } // namespace clang |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| //===------ SemaNVPTX.cpp ------- NVPTX target-specific routines ----------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This file implements semantic analysis functions specific to NVPTX. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "clang/Sema/SemaNVPTX.h" | ||
| #include "clang/Basic/TargetBuiltins.h" | ||
| #include "clang/Sema/Sema.h" | ||
|
|
||
| namespace clang { | ||
|
|
||
| SemaNVPTX::SemaNVPTX(Sema &S) : SemaBase(S) {} | ||
|
|
||
| bool SemaNVPTX::CheckNVPTXBuiltinFunctionCall(const TargetInfo &TI, | ||
| unsigned BuiltinID, | ||
| CallExpr *TheCall) { | ||
| switch (BuiltinID) { | ||
| case NVPTX::BI__nvvm_cp_async_ca_shared_global_4: | ||
| case NVPTX::BI__nvvm_cp_async_ca_shared_global_8: | ||
| case NVPTX::BI__nvvm_cp_async_ca_shared_global_16: | ||
| case NVPTX::BI__nvvm_cp_async_cg_shared_global_16: | ||
| return SemaRef.checkArgCountAtMost(TheCall, 3); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| } // namespace clang |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| //===------ SemaSystemZ.cpp ------ SystemZ target-specific routines -------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This file implements semantic analysis functions specific to SystemZ. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "clang/Sema/SemaSystemZ.h" | ||
| #include "clang/Basic/DiagnosticSema.h" | ||
| #include "clang/Basic/TargetBuiltins.h" | ||
| #include "clang/Sema/Sema.h" | ||
| #include "llvm/ADT/APSInt.h" | ||
| #include <optional> | ||
|
|
||
| namespace clang { | ||
|
|
||
| SemaSystemZ::SemaSystemZ(Sema &S) : SemaBase(S) {} | ||
|
|
||
| bool SemaSystemZ::CheckSystemZBuiltinFunctionCall(unsigned BuiltinID, | ||
| CallExpr *TheCall) { | ||
| if (BuiltinID == SystemZ::BI__builtin_tabort) { | ||
| Expr *Arg = TheCall->getArg(0); | ||
| if (std::optional<llvm::APSInt> AbortCode = | ||
| Arg->getIntegerConstantExpr(getASTContext())) | ||
| if (AbortCode->getSExtValue() >= 0 && AbortCode->getSExtValue() < 256) | ||
| return Diag(Arg->getBeginLoc(), diag::err_systemz_invalid_tabort_code) | ||
| << Arg->getSourceRange(); | ||
| } | ||
|
|
||
| // For intrinsics which take an immediate value as part of the instruction, | ||
| // range check them here. | ||
| unsigned i = 0, l = 0, u = 0; | ||
| switch (BuiltinID) { | ||
| default: return false; | ||
| case SystemZ::BI__builtin_s390_lcbb: i = 1; l = 0; u = 15; break; | ||
| case SystemZ::BI__builtin_s390_verimb: | ||
| case SystemZ::BI__builtin_s390_verimh: | ||
| case SystemZ::BI__builtin_s390_verimf: | ||
| case SystemZ::BI__builtin_s390_verimg: i = 3; l = 0; u = 255; break; | ||
| case SystemZ::BI__builtin_s390_vfaeb: | ||
| case SystemZ::BI__builtin_s390_vfaeh: | ||
| case SystemZ::BI__builtin_s390_vfaef: | ||
| case SystemZ::BI__builtin_s390_vfaebs: | ||
| case SystemZ::BI__builtin_s390_vfaehs: | ||
| case SystemZ::BI__builtin_s390_vfaefs: | ||
| case SystemZ::BI__builtin_s390_vfaezb: | ||
| case SystemZ::BI__builtin_s390_vfaezh: | ||
| case SystemZ::BI__builtin_s390_vfaezf: | ||
| case SystemZ::BI__builtin_s390_vfaezbs: | ||
| case SystemZ::BI__builtin_s390_vfaezhs: | ||
| case SystemZ::BI__builtin_s390_vfaezfs: i = 2; l = 0; u = 15; break; | ||
| case SystemZ::BI__builtin_s390_vfisb: | ||
| case SystemZ::BI__builtin_s390_vfidb: | ||
| return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 15) || | ||
| SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 15); | ||
| case SystemZ::BI__builtin_s390_vftcisb: | ||
| case SystemZ::BI__builtin_s390_vftcidb: i = 1; l = 0; u = 4095; break; | ||
| case SystemZ::BI__builtin_s390_vlbb: i = 1; l = 0; u = 15; break; | ||
| case SystemZ::BI__builtin_s390_vpdi: i = 2; l = 0; u = 15; break; | ||
| case SystemZ::BI__builtin_s390_vsldb: i = 2; l = 0; u = 15; break; | ||
| case SystemZ::BI__builtin_s390_vstrcb: | ||
| case SystemZ::BI__builtin_s390_vstrch: | ||
| case SystemZ::BI__builtin_s390_vstrcf: | ||
| case SystemZ::BI__builtin_s390_vstrczb: | ||
| case SystemZ::BI__builtin_s390_vstrczh: | ||
| case SystemZ::BI__builtin_s390_vstrczf: | ||
| case SystemZ::BI__builtin_s390_vstrcbs: | ||
| case SystemZ::BI__builtin_s390_vstrchs: | ||
| case SystemZ::BI__builtin_s390_vstrcfs: | ||
| case SystemZ::BI__builtin_s390_vstrczbs: | ||
| case SystemZ::BI__builtin_s390_vstrczhs: | ||
| case SystemZ::BI__builtin_s390_vstrczfs: i = 3; l = 0; u = 15; break; | ||
| case SystemZ::BI__builtin_s390_vmslg: i = 3; l = 0; u = 15; break; | ||
| case SystemZ::BI__builtin_s390_vfminsb: | ||
| case SystemZ::BI__builtin_s390_vfmaxsb: | ||
| case SystemZ::BI__builtin_s390_vfmindb: | ||
| case SystemZ::BI__builtin_s390_vfmaxdb: i = 2; l = 0; u = 15; break; | ||
| case SystemZ::BI__builtin_s390_vsld: i = 2; l = 0; u = 7; break; | ||
| case SystemZ::BI__builtin_s390_vsrd: i = 2; l = 0; u = 7; break; | ||
| case SystemZ::BI__builtin_s390_vclfnhs: | ||
| case SystemZ::BI__builtin_s390_vclfnls: | ||
| case SystemZ::BI__builtin_s390_vcfn: | ||
| case SystemZ::BI__builtin_s390_vcnf: i = 1; l = 0; u = 15; break; | ||
| case SystemZ::BI__builtin_s390_vcrnfs: i = 2; l = 0; u = 15; break; | ||
| } | ||
| return SemaRef.BuiltinConstantArgRange(TheCall, i, l, u); | ||
| } | ||
|
|
||
| } // namespace clang |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,341 @@ | ||
| //===------ SemaWasm.cpp ---- WebAssembly target-specific routines --------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This file implements semantic analysis functions specific to WebAssembly. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "clang/Sema/SemaWasm.h" | ||
| #include "clang/AST/ASTContext.h" | ||
| #include "clang/AST/Decl.h" | ||
| #include "clang/AST/Type.h" | ||
| #include "clang/Basic/AddressSpaces.h" | ||
| #include "clang/Basic/DiagnosticSema.h" | ||
| #include "clang/Basic/TargetBuiltins.h" | ||
| #include "clang/Sema/Attr.h" | ||
| #include "clang/Sema/Sema.h" | ||
|
|
||
| namespace clang { | ||
|
|
||
| SemaWasm::SemaWasm(Sema &S) : SemaBase(S) {} | ||
|
|
||
| /// Checks the argument at the given index is a WebAssembly table and if it | ||
| /// is, sets ElTy to the element type. | ||
| static bool CheckWasmBuiltinArgIsTable(Sema &S, CallExpr *E, unsigned ArgIndex, | ||
| QualType &ElTy) { | ||
| Expr *ArgExpr = E->getArg(ArgIndex); | ||
| const auto *ATy = dyn_cast<ArrayType>(ArgExpr->getType()); | ||
| if (!ATy || !ATy->getElementType().isWebAssemblyReferenceType()) { | ||
| return S.Diag(ArgExpr->getBeginLoc(), | ||
| diag::err_wasm_builtin_arg_must_be_table_type) | ||
| << ArgIndex + 1 << ArgExpr->getSourceRange(); | ||
| } | ||
| ElTy = ATy->getElementType(); | ||
| return false; | ||
| } | ||
|
|
||
| /// Checks the argument at the given index is an integer. | ||
| static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E, | ||
| unsigned ArgIndex) { | ||
| Expr *ArgExpr = E->getArg(ArgIndex); | ||
| if (!ArgExpr->getType()->isIntegerType()) { | ||
| return S.Diag(ArgExpr->getBeginLoc(), | ||
| diag::err_wasm_builtin_arg_must_be_integer_type) | ||
| << ArgIndex + 1 << ArgExpr->getSourceRange(); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| bool SemaWasm::BuiltinWasmRefNullExtern(CallExpr *TheCall) { | ||
| if (TheCall->getNumArgs() != 0) | ||
| return true; | ||
|
|
||
| TheCall->setType(getASTContext().getWebAssemblyExternrefType()); | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| bool SemaWasm::BuiltinWasmRefNullFunc(CallExpr *TheCall) { | ||
| ASTContext &Context = getASTContext(); | ||
| if (TheCall->getNumArgs() != 0) { | ||
| Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args) | ||
| << 0 /*function call*/ << /*expected*/ 0 << TheCall->getNumArgs() | ||
| << /*is non object*/ 0; | ||
| return true; | ||
| } | ||
|
|
||
| // This custom type checking code ensures that the nodes are as expected | ||
| // in order to later on generate the necessary builtin. | ||
| QualType Pointee = Context.getFunctionType(Context.VoidTy, {}, {}); | ||
| QualType Type = Context.getPointerType(Pointee); | ||
| Pointee = Context.getAddrSpaceQualType(Pointee, LangAS::wasm_funcref); | ||
| Type = Context.getAttributedType(attr::WebAssemblyFuncref, Type, | ||
| Context.getPointerType(Pointee)); | ||
| TheCall->setType(Type); | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /// Check that the first argument is a WebAssembly table, and the second | ||
| /// is an index to use as index into the table. | ||
| bool SemaWasm::BuiltinWasmTableGet(CallExpr *TheCall) { | ||
| if (SemaRef.checkArgCount(TheCall, 2)) | ||
| return true; | ||
|
|
||
| QualType ElTy; | ||
| if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy)) | ||
| return true; | ||
|
|
||
| if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1)) | ||
| return true; | ||
|
|
||
| // If all is well, we set the type of TheCall to be the type of the | ||
| // element of the table. | ||
| // i.e. a table.get on an externref table has type externref, | ||
| // or whatever the type of the table element is. | ||
| TheCall->setType(ElTy); | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /// Check that the first argumnet is a WebAssembly table, the second is | ||
| /// an index to use as index into the table and the third is the reference | ||
| /// type to set into the table. | ||
| bool SemaWasm::BuiltinWasmTableSet(CallExpr *TheCall) { | ||
| if (SemaRef.checkArgCount(TheCall, 3)) | ||
| return true; | ||
|
|
||
| QualType ElTy; | ||
| if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy)) | ||
| return true; | ||
|
|
||
| if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1)) | ||
| return true; | ||
|
|
||
| if (!getASTContext().hasSameType(ElTy, TheCall->getArg(2)->getType())) | ||
| return true; | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /// Check that the argument is a WebAssembly table. | ||
| bool SemaWasm::BuiltinWasmTableSize(CallExpr *TheCall) { | ||
| if (SemaRef.checkArgCount(TheCall, 1)) | ||
| return true; | ||
|
|
||
| QualType ElTy; | ||
| if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy)) | ||
| return true; | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /// Check that the first argument is a WebAssembly table, the second is the | ||
| /// value to use for new elements (of a type matching the table type), the | ||
| /// third value is an integer. | ||
| bool SemaWasm::BuiltinWasmTableGrow(CallExpr *TheCall) { | ||
| if (SemaRef.checkArgCount(TheCall, 3)) | ||
| return true; | ||
|
|
||
| QualType ElTy; | ||
| if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy)) | ||
| return true; | ||
|
|
||
| Expr *NewElemArg = TheCall->getArg(1); | ||
| if (!getASTContext().hasSameType(ElTy, NewElemArg->getType())) { | ||
| return Diag(NewElemArg->getBeginLoc(), | ||
| diag::err_wasm_builtin_arg_must_match_table_element_type) | ||
| << 2 << 1 << NewElemArg->getSourceRange(); | ||
| } | ||
|
|
||
| if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 2)) | ||
| return true; | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /// Check that the first argument is a WebAssembly table, the second is an | ||
| /// integer, the third is the value to use to fill the table (of a type | ||
| /// matching the table type), and the fourth is an integer. | ||
| bool SemaWasm::BuiltinWasmTableFill(CallExpr *TheCall) { | ||
| if (SemaRef.checkArgCount(TheCall, 4)) | ||
| return true; | ||
|
|
||
| QualType ElTy; | ||
| if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy)) | ||
| return true; | ||
|
|
||
| if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1)) | ||
| return true; | ||
|
|
||
| Expr *NewElemArg = TheCall->getArg(2); | ||
| if (!getASTContext().hasSameType(ElTy, NewElemArg->getType())) { | ||
| return Diag(NewElemArg->getBeginLoc(), | ||
| diag::err_wasm_builtin_arg_must_match_table_element_type) | ||
| << 3 << 1 << NewElemArg->getSourceRange(); | ||
| } | ||
|
|
||
| if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 3)) | ||
| return true; | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /// Check that the first argument is a WebAssembly table, the second is also a | ||
| /// WebAssembly table (of the same element type), and the third to fifth | ||
| /// arguments are integers. | ||
| bool SemaWasm::BuiltinWasmTableCopy(CallExpr *TheCall) { | ||
| if (SemaRef.checkArgCount(TheCall, 5)) | ||
| return true; | ||
|
|
||
| QualType XElTy; | ||
| if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, XElTy)) | ||
| return true; | ||
|
|
||
| QualType YElTy; | ||
| if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 1, YElTy)) | ||
| return true; | ||
|
|
||
| Expr *TableYArg = TheCall->getArg(1); | ||
| if (!getASTContext().hasSameType(XElTy, YElTy)) { | ||
| return Diag(TableYArg->getBeginLoc(), | ||
| diag::err_wasm_builtin_arg_must_match_table_element_type) | ||
| << 2 << 1 << TableYArg->getSourceRange(); | ||
| } | ||
|
|
||
| for (int I = 2; I <= 4; I++) { | ||
| if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, I)) | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, | ||
| unsigned BuiltinID, | ||
| CallExpr *TheCall) { | ||
| switch (BuiltinID) { | ||
| case WebAssembly::BI__builtin_wasm_ref_null_extern: | ||
| return BuiltinWasmRefNullExtern(TheCall); | ||
| case WebAssembly::BI__builtin_wasm_ref_null_func: | ||
| return BuiltinWasmRefNullFunc(TheCall); | ||
| case WebAssembly::BI__builtin_wasm_table_get: | ||
| return BuiltinWasmTableGet(TheCall); | ||
| case WebAssembly::BI__builtin_wasm_table_set: | ||
| return BuiltinWasmTableSet(TheCall); | ||
| case WebAssembly::BI__builtin_wasm_table_size: | ||
| return BuiltinWasmTableSize(TheCall); | ||
| case WebAssembly::BI__builtin_wasm_table_grow: | ||
| return BuiltinWasmTableGrow(TheCall); | ||
| case WebAssembly::BI__builtin_wasm_table_fill: | ||
| return BuiltinWasmTableFill(TheCall); | ||
| case WebAssembly::BI__builtin_wasm_table_copy: | ||
| return BuiltinWasmTableCopy(TheCall); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| WebAssemblyImportModuleAttr * | ||
| SemaWasm::mergeImportModuleAttr(Decl *D, | ||
| const WebAssemblyImportModuleAttr &AL) { | ||
| auto *FD = cast<FunctionDecl>(D); | ||
|
|
||
| if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportModuleAttr>()) { | ||
| if (ExistingAttr->getImportModule() == AL.getImportModule()) | ||
| return nullptr; | ||
| Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) | ||
| << 0 << ExistingAttr->getImportModule() << AL.getImportModule(); | ||
| Diag(AL.getLoc(), diag::note_previous_attribute); | ||
| return nullptr; | ||
| } | ||
| if (FD->hasBody()) { | ||
| Diag(AL.getLoc(), diag::warn_import_on_definition) << 0; | ||
| return nullptr; | ||
| } | ||
| return ::new (getASTContext()) | ||
| WebAssemblyImportModuleAttr(getASTContext(), AL, AL.getImportModule()); | ||
| } | ||
|
|
||
| WebAssemblyImportNameAttr * | ||
| SemaWasm::mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL) { | ||
| auto *FD = cast<FunctionDecl>(D); | ||
|
|
||
| if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportNameAttr>()) { | ||
| if (ExistingAttr->getImportName() == AL.getImportName()) | ||
| return nullptr; | ||
| Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) | ||
| << 1 << ExistingAttr->getImportName() << AL.getImportName(); | ||
| Diag(AL.getLoc(), diag::note_previous_attribute); | ||
| return nullptr; | ||
| } | ||
| if (FD->hasBody()) { | ||
| Diag(AL.getLoc(), diag::warn_import_on_definition) << 1; | ||
| return nullptr; | ||
| } | ||
| return ::new (getASTContext()) | ||
| WebAssemblyImportNameAttr(getASTContext(), AL, AL.getImportName()); | ||
| } | ||
|
|
||
| void SemaWasm::handleWebAssemblyImportModuleAttr(Decl *D, | ||
| const ParsedAttr &AL) { | ||
| auto *FD = cast<FunctionDecl>(D); | ||
|
|
||
| StringRef Str; | ||
| SourceLocation ArgLoc; | ||
| if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) | ||
| return; | ||
| if (FD->hasBody()) { | ||
| Diag(AL.getLoc(), diag::warn_import_on_definition) << 0; | ||
| return; | ||
| } | ||
|
|
||
| FD->addAttr(::new (getASTContext()) | ||
| WebAssemblyImportModuleAttr(getASTContext(), AL, Str)); | ||
| } | ||
|
|
||
| void SemaWasm::handleWebAssemblyImportNameAttr(Decl *D, const ParsedAttr &AL) { | ||
| auto *FD = cast<FunctionDecl>(D); | ||
|
|
||
| StringRef Str; | ||
| SourceLocation ArgLoc; | ||
| if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) | ||
| return; | ||
| if (FD->hasBody()) { | ||
| Diag(AL.getLoc(), diag::warn_import_on_definition) << 1; | ||
| return; | ||
| } | ||
|
|
||
| FD->addAttr(::new (getASTContext()) | ||
| WebAssemblyImportNameAttr(getASTContext(), AL, Str)); | ||
| } | ||
|
|
||
| void SemaWasm::handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL) { | ||
| ASTContext &Context = getASTContext(); | ||
| if (!isFuncOrMethodForAttrSubject(D)) { | ||
| Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) | ||
| << AL << AL.isRegularKeywordAttribute() << ExpectedFunction; | ||
| return; | ||
| } | ||
|
|
||
| auto *FD = cast<FunctionDecl>(D); | ||
| if (FD->isThisDeclarationADefinition()) { | ||
| Diag(D->getLocation(), diag::err_alias_is_definition) << FD << 0; | ||
| return; | ||
| } | ||
|
|
||
| StringRef Str; | ||
| SourceLocation ArgLoc; | ||
| if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) | ||
| return; | ||
|
|
||
| D->addAttr(::new (Context) WebAssemblyExportNameAttr(Context, AL, Str)); | ||
| D->addAttr(UsedAttr::CreateImplicit(Context)); | ||
| } | ||
|
|
||
| } // namespace clang |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| // RUN: %clang_cc1 -ast-print %s -o - | FileCheck %s | ||
|
|
||
| // CHECK: extern "C" int printf(const char *, ...); | ||
| extern "C" int printf(const char *...); | ||
|
|
||
| // CHECK: extern "C++" int f(int); | ||
| // CHECK-NEXT: extern "C++" int g(int); | ||
| extern "C++" int f(int), g(int); | ||
|
|
||
| // CHECK: extern "C" char a; | ||
| // CHECK-NEXT: extern "C" char b; | ||
| extern "C" char a, b; | ||
|
|
||
| // CHECK: extern "C" { | ||
| // CHECK-NEXT: void foo(); | ||
| // CHECK-NEXT: int x; | ||
| // CHECK-NEXT: int y; | ||
| // CHECK-NEXT: extern short z; | ||
| // CHECK-NEXT: } | ||
| extern "C" { | ||
| void foo(void); | ||
| int x, y; | ||
| extern short z; | ||
| } | ||
|
|
||
| // CHECK: extern "C" { | ||
| // CHECK-NEXT: } | ||
| extern "C" {} | ||
|
|
||
| // CHECK: extern "C++"; | ||
| extern "C++"; |