diff --git a/py/emitbc.c b/py/emitbc.c
index cfaea7c88a848701dd48a6445ed68f9b0d239a2b..841dd4aabb57678b13b7ed7599e921b2318d86ed 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -352,6 +352,10 @@ STATIC void emit_bc_adjust_stack_size(emit_t *emit, int delta) {
 STATIC void emit_bc_set_source_line(emit_t *emit, int source_line) {
     //printf("source: line %d -> %d  offset %d -> %d\n", emit->last_source_line, source_line, emit->last_source_line_offset, emit->bytecode_offset);
 #if MICROPY_ENABLE_SOURCE_LINE
+    if (mp_optimise_value >= 3) {
+        // If we compile with -O3, don't store line numbers.
+        return;
+    }
     if (source_line > emit->last_source_line) {
         uint bytes_to_skip = emit->bytecode_offset - emit->last_source_line_offset;
         uint lines_to_skip = source_line - emit->last_source_line;
diff --git a/py/lexer.c b/py/lexer.c
index 4a7ac071b42483331debf936b9de7031b55da65c..26993922eb00d4857d9f38170b9cdb056ce6e8f6 100644
--- a/py/lexer.c
+++ b/py/lexer.c
@@ -64,12 +64,7 @@ struct _mp_lexer_t {
     mp_token_t tok_cur;
 };
 
-// debug flag for __debug__ constant
-STATIC mp_token_kind_t mp_debug_value;
-
-void mp_set_debug(bool value) {
-    mp_debug_value = value ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE;
-}
+uint mp_optimise_value;
 
 // TODO replace with a call to a standard function
 bool str_strn_equal(const char *str, const char *strn, int len) {
@@ -703,7 +698,7 @@ STATIC void mp_lexer_next_token_into(mp_lexer_t *lex, mp_token_t *tok, bool firs
             if (str_strn_equal(tok_kw[i], tok->str, tok->len)) {
                 if (i == ARRAY_SIZE(tok_kw) - 1) {
                     // tok_kw[ARRAY_SIZE(tok_kw) - 1] == "__debug__"
-                    tok->kind = mp_debug_value;
+                    tok->kind = (mp_optimise_value == 0 ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE);
                 } else {
                     tok->kind = MP_TOKEN_KW_FALSE + i;
                 }
diff --git a/py/lexer.h b/py/lexer.h
index b302cb2db15fc6e9b73c4089efa2d05aeeda9f6b..24cae1e25293a7d704c14360876d3fd3d97b6107 100644
--- a/py/lexer.h
+++ b/py/lexer.h
@@ -176,3 +176,5 @@ typedef enum {
 
 mp_import_stat_t mp_import_stat(const char *path);
 mp_lexer_t *mp_lexer_new_from_file(const char *filename);
+
+extern uint mp_optimise_value;
diff --git a/py/runtime.c b/py/runtime.c
index ecaf40deb43ecc8321a243aae0511d3f463433d9..27a5ed54392a04b24c263c875aae27529c994d59 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -45,6 +45,7 @@
 #include "bc.h"
 #include "smallint.h"
 #include "objgenerator.h"
+#include "lexer.h"
 
 #if 0 // print debugging info
 #define DEBUG_PRINT (1)
@@ -74,8 +75,8 @@ void mp_init(void) {
     MICROPY_PORT_INIT_FUNC;
 #endif
 
-    // __debug__ enabled by default
-    mp_set_debug(true);
+    // optimization disabled by default
+    mp_optimise_value = 0;
 
     // init global module stuff
     mp_module_init();
diff --git a/py/runtime.h b/py/runtime.h
index 3c79b48ed0d50ea18cd98d3cfefb5ec8a30fa768..dbd413180b7e4575e2165a557839cb0add7e5b81 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -54,8 +54,6 @@ typedef struct _mp_arg_t {
 void mp_init(void);
 void mp_deinit(void);
 
-void mp_set_debug(bool value); // sets the value of __debug__; see lexer.c
-
 void mp_arg_check_num(uint n_args, uint n_kw, uint n_args_min, uint n_args_max, bool takes_kw);
 void mp_arg_parse_all(uint n_pos, const mp_obj_t *pos, mp_map_t *kws, uint n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals);
 void mp_arg_parse_all_kw_array(uint n_pos, uint n_kw, const mp_obj_t *args, uint n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals);
diff --git a/py/vm.c b/py/vm.c
index cfb390bae728ade1c2037f68206288b5ca45932d..d57fbf17e3bcf9a6b2a505b03bfb3ffc3dc9ce72 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -1042,12 +1042,16 @@ exception_handler:
                 machine_uint_t code_info_size = code_info[0] | (code_info[1] << 8) | (code_info[2] << 16) | (code_info[3] << 24);
                 qstr source_file = code_info[4] | (code_info[5] << 8) | (code_info[6] << 16) | (code_info[7] << 24);
                 qstr block_name = code_info[8] | (code_info[9] << 8) | (code_info[10] << 16) | (code_info[11] << 24);
-                machine_uint_t source_line = 1;
+                machine_uint_t source_line = 0;
                 machine_uint_t bc = code_state->ip - code_info - code_info_size;
                 //printf("find %lu %d %d\n", bc, code_info[12], code_info[13]);
-                for (const byte* ci = code_info + 12; *ci && bc >= ((*ci) & 31); ci++) {
-                    bc -= *ci & 31;
-                    source_line += *ci >> 5;
+                const byte* ci = code_info + 12;
+                if (*ci) {
+                    source_line = 1;
+                    for (; *ci && bc >= ((*ci) & 31); ci++) {
+                        bc -= *ci & 31;
+                        source_line += *ci >> 5;
+                    }
                 }
                 mp_obj_exception_add_traceback(nlr.ret_val, source_file, source_line, block_name);
             }
diff --git a/unix/main.c b/unix/main.c
index 992ea1fec566f012dea007e98681c13ab0823743..7c3fbf6aa1530f7c678d2c674363f4553af73862 100644
--- a/unix/main.c
+++ b/unix/main.c
@@ -30,6 +30,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <ctype.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <errno.h>
@@ -188,6 +189,7 @@ int usage(char **argv) {
 "usage: %s [<opts>] [-X <implopt>] [-c <command>] [<filename>]\n"
 "Options:\n"
 "-v : verbose (trace various operations); can be multiple\n"
+"-O[N] : apply bytecode optimizations of level N\n"
 "\n"
 "Implementation specific options:\n", argv[0]
 );
@@ -346,9 +348,13 @@ int main(int argc, char **argv) {
                 a += 1;
             } else if (strcmp(argv[a], "-v") == 0) {
                 mp_verbose_flag++;
-            } else if (strcmp(argv[a], "-O") == 0) {
-                // optimisation; sets __debug__ to False
-                mp_set_debug(false);
+            } else if (strncmp(argv[a], "-O", 2) == 0) {
+                if (isdigit(argv[a][2])) {
+                    mp_optimise_value = argv[a][2] & 0xf;
+                } else {
+                    mp_optimise_value = 0;
+                    for (char *p = argv[a] + 1; *p && *p == 'O'; p++, mp_optimise_value++);
+                }
             } else {
                 return usage(argv);
             }