diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtoi.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtoi.java
index cbca860f401407a3c234193721821ec4f3a043b1..ce5653198a3d926047cf1bafafbb16a6b17e4808 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtoi.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtoi.java
@@ -22,11 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.eq;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.ExactMath;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.profiles.PrimitiveValueProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -34,53 +42,108 @@ import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 @RBuiltin(name = "strtoi", kind = INTERNAL, parameterNames = {"x", "base"}, behavior = PURE)
 public abstract class Strtoi extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        // TODO: not sure if the behavior is 100% compliant
-        casts.arg("base").asIntegerVector().findFirst();
+        casts.arg("x").mustBe(stringValue()).asStringVector();
+        // base == 0 || (base >= 2 && base <= 36)
+        casts.arg("base").mustBe(integerValue()).asIntegerVector().findFirst().mustBe(eq(0).or(gte(2).and(lte(36))));
     }
 
     @Specialization
-    @TruffleBoundary
-    protected RIntVector doStrtoi(RAbstractStringVector vec, int baseArg) {
-        int base = baseArg;
+    protected RIntVector doStrtoi(RAbstractStringVector vec, int baseArg,
+                    @Cached("createBinaryProfile()") ConditionProfile emptyProfile,
+                    @Cached("createBinaryProfile()") ConditionProfile baseZeroProfile,
+                    @Cached("createBinaryProfile()") ConditionProfile negateProfile,
+                    @Cached("createBinaryProfile()") ConditionProfile incompleteProfile,
+                    @Cached("createEqualityProfile()") PrimitiveValueProfile baseProfile) {
         int[] data = new int[vec.getLength()];
-        boolean complete = RDataFactory.COMPLETE_VECTOR;
+        boolean complete = true;
         for (int i = 0; i < data.length; i++) {
-            int dataValue = RRuntime.INT_NA;
-            try {
-                String s = vec.getDataAt(i);
-                if (s.length() == 0) {
-                    complete = RDataFactory.INCOMPLETE_VECTOR;
+            int dataValue;
+            String s = vec.getDataAt(i);
+            if (emptyProfile.profile(s.length() == 0)) {
+                dataValue = RRuntime.INT_NA;
+            } else {
+                boolean negate = false;
+                int pos = 0;
+                if (s.charAt(pos) == '+') {
+                    // skip "+"
+                    pos++;
+                } else if (s.charAt(pos) == '-') {
+                    negate = true;
+                    pos++;
+                }
+                int base = baseArg;
+                if (pos < s.length() && s.charAt(pos) == '0') {
+                    // skip "0"
+                    pos++;
+                    if (pos < s.length() && (s.charAt(pos) == 'x' || s.charAt(pos) == 'X')) {
+                        if (baseZeroProfile.profile(base == 0)) {
+                            base = 16;
+                        }
+                        // skip "x" or "X"
+                        pos++;
+                    } else {
+                        if (baseZeroProfile.profile(base == 0)) {
+                            base = 8;
+                        }
+                        // go back (to parse the "0")
+                        pos--;
+                    }
+                } else {
+                    if (baseZeroProfile.profile(base == 0)) {
+                        base = 10;
+                    }
+                }
+                base = baseProfile.profile(base);
+                if (pos == s.length()) {
+                    // produce NA is no data is available
+                    dataValue = RRuntime.INT_NA;
                 } else {
-                    if (base == 0) {
-                        char ch0 = s.charAt(0);
-                        if (ch0 == '0') {
-                            if (s.length() > 1 && (s.charAt(1) == 'x' || s.charAt(1) == 'X')) {
-                                base = 16;
+                    dataValue = 0;
+                    while (pos < s.length()) {
+                        char c = s.charAt(pos++);
+                        int digit;
+                        if (base > 10) {
+                            if (c >= '0' && c <= '9') {
+                                digit = c - '0';
+                            } else if (c >= 'a' && c < ('a' + base - 10)) {
+                                digit = c - 'a' + 10;
+                            } else if (c >= 'A' && c < ('A' + base - 10)) {
+                                digit = c - 'A' + 10;
                             } else {
-                                base = 8;
+                                dataValue = RRuntime.INT_NA;
+                                break;
                             }
                         } else {
-                            base = 10;
+                            if (c >= '0' && c < ('0' + base)) {
+                                digit = c - '0';
+                            } else {
+                                dataValue = RRuntime.INT_NA;
+                                break;
+                            }
+                        }
+                        try {
+                            dataValue = ExactMath.addExact(ExactMath.multiplyExact(dataValue, base), digit);
+                        } catch (ArithmeticException e) {
+                            dataValue = RRuntime.INT_NA;
+                            break;
                         }
                     }
-                    long value = RFFIFactory.getRFFI().getBaseRFFI().strtol(s, base);
-                    if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
-                        complete = RDataFactory.INCOMPLETE_VECTOR;
-                    } else {
-                        dataValue = (int) value;
-                    }
                 }
-            } catch (IllegalArgumentException ex) {
-                complete = RDataFactory.INCOMPLETE_VECTOR;
+                if (negateProfile.profile(negate)) {
+                    // relies on -INT_NA == INT_NA
+                    dataValue = -dataValue;
+                }
+                if (incompleteProfile.profile(dataValue == RRuntime.INT_NA)) {
+                    complete = false;
+                }
+                data[i] = dataValue;
             }
-            data[i] = dataValue;
         }
         return RDataFactory.createIntVector(data, complete);
     }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtoi.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtoi.java
index 716705d3dcf12e4f68f1130b97fe3324a2597551..b16c9f5067819a12e38ef10d313a145aaf993e95 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtoi.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtoi.java
@@ -29,12 +29,12 @@ public class TestBuiltin_strtoi extends TestBase {
 
     @Test
     public void teststrtoi3() {
-        assertEval(Ignored.Unknown, "argv <- list(c('0xff', '077', '123'), 0L); .Internal(strtoi(argv[[1]], argv[[2]]))");
+        assertEval("argv <- list(c('0xff', '077', '123'), 0L); .Internal(strtoi(argv[[1]], argv[[2]]))");
     }
 
     @Test
     public void teststrtoi4() {
-        assertEval(Ignored.Unknown, "argv <- list('1.3', 16L); .Internal(strtoi(argv[[1]], argv[[2]]))");
+        assertEval("argv <- list('1.3', 16L); .Internal(strtoi(argv[[1]], argv[[2]]))");
     }
 
     @Test