Add test cases and polish change
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 0d70fa8..a36852a 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2742,18 +2742,17 @@
     This attribute indicates that outlining passes should not modify the
     function.
 
-``"modular-format"="<type>,<string_idx>,<first_idx_to_check>,<modular_impl_fn>,<impl_name>,<aspects...>"``
+``"modular-format"="<type>,<string_idx>,<first_arg_idx>,<modular_impl_fn>,<impl_name>,<aspects...>"``
     This attribute indicates that the implementation is modular on a particular
-    format string argument . When the argument for a given call is constant, the
-    compiler may redirect the call to a modular implementation function
-    instead.
+    format string argument. If the compiler can determine that not all aspects
+    of the implementation are needed, it can report which aspects were needed
+    and redirect the call to a modular implementation function instead.
 
-    The compiler also emits relocations to report various aspects of the format
-    string and arguments that were present. The compiler reports an aspect by
-    issuing a relocation for the symbol `<impl_name>_<aspect>``. This arranges
-    for code and data needed to support the aspect of the implementation to be
-    brought into the link to satisfy weak references in the modular
-    implemenation function.
+    The compiler reports that an implementation aspect is needed by issuing a
+    relocation for the symbol `<impl_name>_<aspect>``. This arranges for code
+    and data needed to support the aspect of the implementation to be brought
+    into the link to satisfy weak references in the modular implemenation
+    function.
 
     The first three arguments have the same semantics as the arguments to the C
     ``format`` attribute.
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 2e08c14..f26ccc1 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2554,6 +2554,20 @@
       CheckFailed("invalid value for 'denormal-fp-math-f32' attribute: " + S,
                   V);
   }
+
+  if (auto A = Attrs.getFnAttr("modular-format"); A.isValid()) {
+    StringRef S = A.getValueAsString();
+    SmallVector<StringRef> Args;
+    S.split(Args, ',');
+    Check(Args.size() >= 5,
+          "modular-format attribute requires at least 5 arguments", V);
+    unsigned FirstArgIdx;
+    Check(!Args[2].getAsInteger(10, FirstArgIdx),
+          "modular-format attribute first arg index is not an integer", V);
+    unsigned UpperBound = FT->getNumParams() + (FT->isVarArg() ? 1 : 0);
+    Check(FirstArgIdx > 0 && FirstArgIdx <= UpperBound,
+          "modular-format attribute first arg index is out of bounds", V);
+  }
 }
 void Verifier::verifyUnknownProfileMetadata(MDNode *MD) {
   Check(MD->getNumOperands() == 2,
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 73f2aeb..6f71dcd 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -4079,12 +4079,10 @@
   SmallVector<StringRef> Args(
       llvm::split(CI->getFnAttr("modular-format").getValueAsString(), ','));
   // TODO: Make use of the first two arguments
-  // TODO: Error handling
   unsigned FirstArgIdx;
-  if (!llvm::to_integer(Args[2], FirstArgIdx))
-    return nullptr;
-  if (FirstArgIdx == 0)
-    return nullptr;
+  [[maybe_unused]] bool Error;
+  Error = Args[2].getAsInteger(10, FirstArgIdx);
+  assert(!Error && "invalid first arg index");
   --FirstArgIdx;
   StringRef FnName = Args[3];
   StringRef ImplName = Args[4];
diff --git a/llvm/test/Transforms/InstCombine/modular-format.ll b/llvm/test/Transforms/InstCombine/modular-format.ll
new file mode 100644
index 0000000..9b1e60b
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/modular-format.ll
@@ -0,0 +1,104 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Test that the modular format string library call simplifier works correctly.
+;
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128"
+
+@.str.int = constant [3 x i8] c"%d\00"
+@.str.float = constant [3 x i8] c"%f\00"
+@.str.multi = constant [6 x i8] c"%f %d\00"
+@.str.multifp = constant [6 x i8] c"%f %f\00"
+@.str.noargs = constant [1 x i8] c"\00"
+
+; Basic Transformation
+define void @test_basic(i32 %arg) {
+; CHECK-LABEL: @test_basic(
+; CHECK-NEXT:    call void (ptr, ...) @basic_mod(ptr nonnull @.str.int, i32 [[ARG:%.*]])
+; CHECK-NEXT:    ret void
+;
+  call void (ptr, ...) @basic(ptr @.str.int, i32 %arg)
+  ret void
+}
+
+declare void @basic(ptr, ...) "modular-format"="printf,1,2,basic_mod,basic_impl" 
+; "float" Aspect - Present
+define void @test_float_present(double %arg) {
+; CHECK-LABEL: @test_float_present(
+; CHECK-NEXT:    call void (ptr, ...) @float_present_mod(ptr nonnull @.str.float, double [[ARG:%.*]])
+; CHECK-NEXT:    call void @llvm.reloc.none(ptr nonnull @basic_impl_float)
+; CHECK-NEXT:    ret void
+;
+  call void (ptr, ...) @float_present(ptr @.str.float, double %arg)
+  ret void
+}
+
+declare void @float_present(ptr, ...) #0
+
+; Unknown Aspects
+define void @test_unknown_aspects(i32 %arg) {
+; CHECK-LABEL: @test_unknown_aspects(
+; CHECK-NEXT:    call void (ptr, ...) @unknown_aspects_mod(ptr nonnull @.str.int, i32 [[ARG:%.*]])
+; CHECK-NEXT:    call void @llvm.reloc.none(ptr nonnull @basic_impl_unknown1)
+; CHECK-NEXT:    call void @llvm.reloc.none(ptr nonnull @basic_impl_unknown2)
+; CHECK-NEXT:    ret void
+;
+  call void (ptr, ...) @unknown_aspects(ptr @.str.int, i32 %arg)
+  ret void
+}
+
+declare void @unknown_aspects(ptr, ...) "modular-format"="printf,1,2,unknown_aspects_mod,basic_impl,unknown1,unknown2"
+
+; Multiple Aspects
+define void @test_multiple_aspects(double %arg1, i32 %arg2) {
+; CHECK-LABEL: @test_multiple_aspects(
+; CHECK-NEXT:    call void (ptr, ...) @multiple_aspects_mod(ptr nonnull @.str.multi, double [[ARG1:%.*]], i32 [[ARG2:%.*]])
+; CHECK-NEXT:    call void @llvm.reloc.none(ptr nonnull @basic_impl_float)
+; CHECK-NEXT:    call void @llvm.reloc.none(ptr nonnull @basic_impl_unknown)
+; CHECK-NEXT:    ret void
+;
+  call void (ptr, ...) @multiple_aspects(ptr @.str.multi, double %arg1, i32 %arg2)
+  ret void
+}
+
+declare void @multiple_aspects(ptr, ...) "modular-format"="printf,1,2,multiple_aspects_mod,basic_impl,float,unknown"
+
+; Multiple Floating-Point Arguments
+define void @test_multiple_fp_args(double %arg1, float %arg2) {
+; CHECK-LABEL: @test_multiple_fp_args(
+; CHECK-NEXT:    call void (ptr, ...) @float_present_mod(ptr nonnull @.str.multifp, double [[ARG1:%.*]], float [[ARG2:%.*]])
+; CHECK-NEXT:    call void @llvm.reloc.none(ptr nonnull @basic_impl_float)
+; CHECK-NEXT:    ret void
+;
+  call void (ptr, ...) @multiple_fp_args(ptr @.str.multifp, double %arg1, float %arg2)
+  ret void
+}
+
+declare void @multiple_fp_args(ptr, ...) #0
+
+; No Arguments to Check
+define void @test_no_args_to_check() {
+; CHECK-LABEL: @test_no_args_to_check(
+; CHECK-NEXT:    call void (ptr, ...) @float_present_mod(ptr nonnull @.str.noargs)
+; CHECK-NEXT:    ret void
+;
+  call void (ptr, ...) @no_args_to_check(ptr @.str.noargs)
+  ret void
+}
+
+declare void @no_args_to_check(ptr, ...) #0
+
+; First argument index != 2
+define void @test_first_arg_idx(i32 %ignored, double %arg) {
+; CHECK-LABEL: @test_first_arg_idx(
+; CHECK-NEXT:    call void (i32, ptr, ...) @first_arg_idx_mod(i32 [[IGNORED:%.*]], ptr nonnull @.str.float, double [[ARG:%.*]])
+; CHECK-NEXT:    call void @llvm.reloc.none(ptr nonnull @basic_impl_float)
+; CHECK-NEXT:    ret void
+;
+  call void (i32, ptr, ...) @first_arg_idx(i32 %ignored, ptr @.str.float, double %arg)
+  ret void
+}
+
+declare void @first_arg_idx(i32, ptr, ...) "modular-format"="printf,2,3,first_arg_idx_mod,basic_impl,float"
+
+attributes #0 = { "modular-format"="printf,1,2,float_present_mod,basic_impl,float" }
diff --git a/llvm/test/Verifier/modular-format.ll b/llvm/test/Verifier/modular-format.ll
new file mode 100644
index 0000000..abdd73d
--- /dev/null
+++ b/llvm/test/Verifier/modular-format.ll
@@ -0,0 +1,41 @@
+; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s
+
+define void @test_too_few_arguments(i32 %arg, ...) "modular-format"="printf,1,2,basic_mod" {
+  ret void
+}
+; CHECK: modular-format attribute requires at least 5 arguments
+; CHECK-NEXT: ptr @test_too_few_arguments
+
+define void @test_first_arg_index_not_integer(i32 %arg, ...) "modular-format"="printf,1,foo,basic_mod,basic_impl" {
+  ret void
+}
+; CHECK: modular-format attribute first arg index is not an integer
+; CHECK-NEXT: ptr @test_first_arg_index_not_integer
+
+define void @test_first_arg_index_zero(i32 %arg) "modular-format"="printf,1,0,basic_mod,basic_impl" {
+  ret void
+}
+; CHECK: modular-format attribute first arg index is out of bounds
+; CHECK-NEXT: ptr @test_first_arg_index_zero
+
+define void @test_first_arg_index_out_of_bounds(i32 %arg) "modular-format"="printf,1,2,basic_mod,basic_impl" {
+  ret void
+}
+; CHECK: modular-format attribute first arg index is out of bounds
+; CHECK-NEXT: ptr @test_first_arg_index_out_of_bounds
+
+define void @test_first_arg_index_out_of_bounds_varargs(i32 %arg, ...) "modular-format"="printf,1,3,basic_mod,basic_impl" {
+  ret void
+}
+; CHECK: modular-format attribute first arg index is out of bounds
+; CHECK-NEXT: ptr @test_first_arg_index_out_of_bounds_varargs
+
+; CHECK-NOT: ptr @test_first_arg_index_in_bounds
+define void @test_first_arg_index_in_bounds(i32 %arg) "modular-format"="printf,1,1,basic_mod,basic_impl" {
+  ret void
+}
+
+; CHECK-NOT: ptr @test_first_arg_index_in_bounds_varargs
+define void @test_first_arg_index_in_bounds_varargs(i32 %arg, ...) "modular-format"="printf,1,2,basic_mod,basic_impl" {
+  ret void
+}